생성자에서 템플릿 매개 변수를 추론하지 않는 이유는 무엇입니까? 무엇입니까? 예를

오늘 내 질문은 매우 간단합니다. 컴파일러가 함수 매개 변수에서 할 수있는 것처럼 클래스 생성자에서 템플릿 매개 변수를 추론 할 수없는 이유는 무엇입니까? 예를 들어, 다음 코드가 유효하지 않은 이유는 무엇입니까?

template<typename obj>
class Variable {
      obj data;
      public: Variable(obj d)
              {
                   data = d;
              }
};

int main()
{
    int num = 2;
    Variable var(num); //would be equivalent to Variable<int> var(num),
    return 0;          //but actually a compile error
}

내가 말했듯이 이것이 유효하지 않다는 것을 이해하므로 내 질문은 그렇지 않습니까? 이것이 큰 통사론 적 허점을 만들까요? 이 기능을 원하지 않는 경우가 있습니까 (유형을 추론하면 문제가 발생하는 경우)? 함수에 대한 템플릿 추론을 허용하는 논리를 이해하려고 노력하고 있지만 적절하게 구성된 클래스에는 적용되지 않습니다.



답변

생성자가 항상 클래스의 유일한 진입 지점이 아니기 때문에 유효하지 않다고 생각합니다 (복사 생성자와 operator =에 대해 이야기하고 있습니다). 따라서 다음과 같이 클래스를 사용한다고 가정하십시오.

MyClass m(string s);
MyClass *pm;
*pm = m;

파서가 어떤 템플릿 유형이 MyClass pm인지 아는 것이 그렇게 명백한 지 확실하지 않습니다.

내가 말한 내용이 의미가 있는지 확실하지 않지만 의견을 자유롭게 추가 할 수 있습니다. 흥미로운 질문입니다.

C ++ 17

C ++ 17은 생성자 인수에서 유형 추론을 할 수 있습니다.

예 :

std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);

허용 된 종이 .


답변

다른 사람들이 언급 한 이유 때문에 요청한 것을 할 수는 없지만 다음과 같이 할 수 있습니다.

template<typename T>
class Variable {
    public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
  return Variable<T>(instance);
}

모든 의도와 목적을 위해 요청하는 것과 동일합니다. 캡슐화를 좋아한다면 make_variable을 정적 멤버 함수로 만들 수 있습니다. 이것이 사람들이 명명 된 생성자라고 부르는 것입니다. 따라서 원하는 작업을 수행 할뿐만 아니라 원하는 작업을 거의 수행 할 수 있습니다. 컴파일러는 (명명 된) 생성자에서 템플릿 매개 변수를 유추합니다.

주의 : 합리적인 컴파일러는 다음과 같이 작성할 때 임시 객체를 최적화합니다.

auto v = make_variable(instance);


답변

이 질문이 제기 된 이후 두 가지 새로운 표준과 곧 새로운 표준이있는 2016 년의 계몽 된 시대에 알아야 할 중요한 것은 C ++ 17 표준을 지원 하는 컴파일러가 코드를 그대로 컴파일 한다는 것 입니다. .

C ++ 17의 클래스 템플릿에 대한 템플릿 인수 추론

여기 (수락 된 답변에 대한 Olzhas Zhumabek의 편집에 의함)는 표준에 대한 관련 변경 사항을 자세히 설명하는 문서입니다.

다른 답변의 문제 해결

현재 최고 등급 답변

이 답변은 “복사 생성자 및 operator=“가 올바른 템플릿 전문화를 알지 못함을 나타냅니다.

표준 복사 생성자 가 알려진 템플릿 유형에 operator= 대해서만 존재 하기 때문에 이것은 말도 안됩니다 .

template <typename T>
class MyClass {
    MyClass(const MyClass&) =default;
    ... etc...
};

// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm;   // WHAT IS THIS?
*pm = m;

내가 코멘트에서 언급 한 바와 같이 여기,이없는 이유 에 대한 MyClass *pm또는 추론의 새로운 형태없이 법적 선언 할 수는 : MyClass 유형없는 이의 포인터를 선언하는 이해가되지 않도록, (그것은 템플릿입니다) 유형 MyClass. 다음은 예를 수정할 수있는 한 가지 방법입니다.

MyClass m(string("blah blah blah"));
decltype(m) *pm;               // uses type inference!
*pm = m;

여기서, pm이다 이미 추론이 사소한 올바른 유형의, 그리고 있도록. 또한 복사 생성자를 호출 할 때 실수로 유형을 혼합하는 것은 불가능합니다 .

MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));

여기 pm에의 복사본에 대한 포인터가 있습니다 m. 여기서, MyClass복사 – 구성되는 m– 어떤 유형이다 MyClass<string>(그리고 되지 존재하지 않는 타입 MyClass). 따라서, 지점 pm의 유형을 유추됩니다, 거기 이다 의 템플릿 형 있음을 알 수있는 충분한 정보 m, 따라서의 템플릿 유형 pm이다가 string.

또한 다음은 항상 컴파일 오류를 발생시킵니다 .

MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;

복사 생성자의 선언이 있기 때문입니다 하지 템플릿 :

MyClass(const MyClass&);

여기서 copy-constructor 인자의 template-type 전체 클래스 의 template-type 과 일치 합니다. 즉이 경우, MyClass<string>인스턴스화, MyClass<string>::MyClass(const MyClass<string>&);그것을 인스턴스화되고, 때 MyClass<int>인스턴스화, MyClass<int>::MyClass(const MyClass<int>&);인스턴스화됩니다. 명시 적으로 지정되거나 템플릿 화 된 생성자가 선언되지 않는 한 컴파일러가을 인스턴스화 할 이유가 없으며 MyClass<int>::MyClass(const MyClass<string>&);이는 분명히 부적절합니다.

Cătălin Pitiș의 답변

Pitiș는 및를 추론하는 예제를 제공하고 다음 Variable<int>Variable<double>같이 말합니다.

두 가지 유형 (Variable 및 Variable)의 코드에 동일한 유형 이름 (Variable)이 있습니다. 내 주관적인 관점에서 볼 때 코드의 가독성에 거의 영향을 미칩니다.

이전 예제에서 언급했듯이 , 새로운 기능이 구문 상 하나처럼 보이더라도 Variable그 자체는 유형 이름 이 아닙니다 .

그런 다음 Pitiș는 적절한 추론을 허용하는 생성자가 제공되지 않으면 어떻게 될 것인지 묻습니다. 대답은 추론이 생성자 호출 에 의해 트리거되기 때문에 추론이 허용되지 않는다는 것입니다 . 생성자 호출이 없으면 추론없습니다 .

이것은 foo여기서 추론 된 버전을 묻는 것과 유사합니다 .

template <typename T> foo();
foo();

대답은 명시된 이유 때문에이 코드가 불법이라는 것입니다.

MSalter의 답변

이것이 제가 말할 수있는 한, 제안 된 기능에 대한 합법적 인 우려를 제기하는 유일한 대답입니다.

예는 다음과 같습니다.

Variable var(num);  // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?

핵심 질문은 컴파일러가 여기서 유형 유추 생성자를 선택 합니까 아니면 복사 생성자를 선택합니까?

코드를 시도해 보면 복사 생성자가 선택되었음을 알 수 있습니다. 예제를 확장하려면 :

Variable var(num);          // infering ctor
Variable var2(var);         // copy ctor
Variable var3(move(var));   // move ctor
// Variable var4(Variable(num));     // compiler error

나는 제안과 표준의 새 버전이 이것을 어떻게 명시하고 있는지 잘 모르겠습니다. 그것은 내가 아직 이해하지 못하는 새로운 표준 인 “추론 가이드”에 의해 결정된 것 같습니다.

나는 또한 var4공제가 왜 불법 인지 잘 모르겠습니다 . g ++의 컴파일러 오류는 명령문이 함수 선언으로 구문 분석되고 있음을 나타내는 것 같습니다.


답변

여전히 누락 됨 : 다음 코드를 매우 모호하게 만듭니다.

int main()
{
    int num = 2;
    Variable var(num);  // If equivalent to Variable<int> var(num),
    Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}


답변

컴파일러가 요청한 것을 지원한다고 가정합니다. 그러면이 코드가 유효합니다.

Variable v1( 10); // Variable<int>

// Some code here

Variable v2( 20.4); // Variable<double>

이제 두 가지 유형 (Variable 및 Variable)에 대한 코드에 동일한 유형 이름 (Variable)이 있습니다. 내 주관적인 관점에서 볼 때 코드의 가독성에 거의 영향을 미칩니다. 동일한 네임 스페이스에서 두 개의 다른 유형에 대해 동일한 유형 이름을 갖는 것은 오해의 소지가있는 것 같습니다.

나중에 업데이트 :
고려해야 할 또 다른 사항 : 부분 (또는 전체) 템플릿 전문화입니다.

내가 Variable을 전문화하고 예상 한 생성자를 제공하지 않으면 어떻게됩니까?

그래서 나는 :

template<>
class Variable<int>
{
// Provide default constructor only.
};

그런 다음 코드가 있습니다.

Variable v( 10);

컴파일러는 무엇을해야합니까? 일반 변수 클래스 정의를 사용하여 그것이 변수임을 추론 한 다음 변수가 하나의 매개 변수 생성자를 제공하지 않는다는 것을 발견합니까?


답변

C ++ 03 및 C ++ 11 표준은 생성자에게 전달 된 매개 변수에서 템플릿 인수 추론을 허용하지 않습니다.

그러나 “생성자를위한 템플릿 매개 변수 추론”에 대한 제안이 있으므로 곧 원하는 것을 얻을 수 있습니다. 편집 : 실제로이 기능은 C ++ 17에서 확인되었습니다.

참조 : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.htmlhttp://www.open-std.org/jtc1/sc22/wg21/docs/ papers / 2015 / p0091r0.html


답변

많은 클래스가 생성자 매개 변수에 의존하지 않습니다. 생성자가 하나만 있고이 생성자의 유형에 따라 매개 변수화하는 클래스는 몇 개뿐입니다.

템플릿 추론이 정말 필요한 경우 도우미 함수를 사용하세요.

template<typename obj>
class Variable
{
      obj data;
public:
      Variable(obj d)
      : data(d)
      { }
};

template<typename obj>
inline Variable<obj> makeVariable(const obj& d)
{
    return Variable<obj>(d);
}