생성자에서 템플릿 매개 변수를 추론하지 않는 이유는 무엇입니까?
오늘 내 질문은 매우 간단합니다. 컴파일러가 함수 매개 변수에서 할 수있는 것처럼 클래스 생성자에서 템플릿 매개 변수를 추론 할 수없는 이유는 무엇입니까? 예를 들어, 다음 코드가 유효하지 않은 이유는 무엇입니까?
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을 정적 멤버 함수로 만들 수 있습니다. 이것이 사람들이 명명 된 생성자라고 부르는 것입니다. 따라서 원하는 작업을 수행 할뿐만 아니라 원하는 작업을 거의 수행 할 수 있습니다. 컴파일러는 (명명 된) 생성자에서 템플릿 매개 변수를 유추합니다.
주의 : 합리적인 컴파일러는 다음과 같이 작성할 때 임시 객체를 최적화합니다.
Variable<T> 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;
Here, pm
is already of the correct type, and so the inference is trivial. Moreover, it's impossible to accidentally mix types when calling the copy-constructor:
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
Here, pm
will be a pointer to a copy of m
. Here, MyClass
is being copy-constructed from m
—which is of type MyClass<string>
(and not of the nonexistent type MyClass
). Thus, at the point where pm
's type is inferred, there is sufficient information to know that the template-type of m
, and therefore the template-type of pm
, is string
.
Moreover, the following will always raise a compile error:
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
This is because the declaration of the copy constructor is not templated:
MyClass(const MyClass&);
Here, the copy-constructor argument's template-type matches the template-type of the class overall; i.e., when MyClass<string>
is instantiated, MyClass<string>::MyClass(const MyClass<string>&);
is instantiated with it, and when MyClass<int>
is instantiated, MyClass<int>::MyClass(const MyClass<int>&);
is instantiated. Unless it is explicitly specified or a templatized constructor is declared, there is no reason for the compiler to instantiate MyClass<int>::MyClass(const MyClass<string>&);
, which would obviously be inappropriate.
The answer by Cătălin Pitiș
Pitiș gives an example deducing Variable<int>
and Variable<double>
, then states:
I have the same type name (Variable) in the code for two different types (Variable and Variable). From my subjective point of view, it affects the readability of the code pretty much.
As noted in the previous example, Variable
itself is not a type name, even though the new feature makes it look like one syntactically.
Pitiș then asks what would happen if no constructor is given that would permit the appropriate inference. The answer is that no inference is permitted, because the inference is triggered by the constructor call. Without a constructor-call, there is no inference.
This is similar to asking what version of foo
is deduced here:
template <typename T> foo();
foo();
The answer is that this code is illegal, for the reason stated.
MSalter's answer
This is, as far as I can tell, the only answer to bring up a legitimate concern about the proposed feature.
The example is:
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
The key question is, does the compiler select the type-inferred constructor here or the copy constructor?
Trying the code out, we can see that the copy constructor is selected. To expand on the example:
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error
I am not sure how the proposal and the new version of the standard specify this; it appears to be determined by "deduction guides," which are a new bit of standardese that I don't yet understand.
I am also not sure why the var4
deduction is illegal; the compiler error from g++ seems to indicate that the statement is being parsed as a function declaration.
Still missing: It makes the following code quite ambiguous:
int main()
{
int num = 2;
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}
Supposing that the compiler supports what you asked. Then this code is valid:
Variable v1( 10); // Variable<int>
// Some code here
Variable v2( 20.4); // Variable<double>
Now, I have the same type name (Variable) in the code for two different types (Variable and Variable). From my subjective point of view, it affects the readability of the code pretty much. Having same type name for two different types in the same namespace looks misleading to me.
Later update: Another thing to consider: partial (or full) template specialization.
What if I specialize Variable and provide no constructor like you expect?
So I would have:
template<>
class Variable<int>
{
// Provide default constructor only.
};
Then I have the code:
Variable v( 10);
What should the compiler do? Use generic Variable class definition to deduce that it is Variable, then discover that Variable doesn't provide one parameter constructor?
The C++03 and the C++11 standard does not allow for template argument deduction from the parameters passed to the constuructor.
But there is a proposal for "Template parameter deduction for constructors" so you may get what you are asking for soon. Edit: indeed, this feature has been confirmed for C++17.
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html
A lot of classes don't depend on constructor parameters. There are only a few classes that have only one constructor, and parameterize based on this constructor's type(s).
If you really need template inference, use a helper function:
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);
}
Deduction of types is limited to template functions in current C++, but it's long been realised that type deduction in other contexts would be very useful. Hence C++0x's auto
.
While exactly what you suggest won't be possible in C++0x, the following shows you can get pretty close:
template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
// remove reference required for the case that x is an lvalue
return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}
void test()
{
auto v = MakeVariable(2); // v is of type Variable<int>
}
You are right the compiler could easily guess, but it's not in the standard or C++0x as far as I know so you'll have to wait atleast 10 more years (ISO standards fixed turn around rate) before compiller providers add this feature
Let's look at the problem with reference to a class everyone should be familar with - std::vector.
Firstly, a very common use of vector is to use the constructor that takes no parameters:
vector <int> v;
In this case, obviously no inference can be performed.
A second common use is to create a pre-sized vector:
vector <string> v(100);
Here, if inference were used:
vector v(100);
we get a vector of ints, not strings, and presumably it isn't sized!
Lastly, consider constructors that take multiple parameters - with "inference":
vector v( 100, foobar() ); // foobar is some class
Which parameter should be used for inference? We would need some way of telling the compiler that it should be the second one.
With all these problems for a class as simple as vector, it's easy to see why inference is not used.
Making the ctor a template the Variable can have only one form but various ctors:
class Variable {
obj data; // let the compiler guess
public:
template<typename obj>
Variable(obj d)
{
data = d;
}
};
int main()
{
int num = 2;
Variable var(num); // Variable::data int?
float num2 = 2.0f;
Variable var2(num2); // Variable::data float?
return 0;
}
See? We can not have multiple Variable::data members.
See The C++ Template Argument Deduction for more info on this.
참고URL : https://stackoverflow.com/questions/984394/why-not-infer-template-parameter-from-constructor
'developer tip' 카테고리의 다른 글
IHttpHandler.IsReusable의 용도는 무엇입니까? (0) | 2020.08.18 |
---|---|
좋은 부울 표현식 단순화가 있습니까? (0) | 2020.08.18 |
자바 프로그래밍-SQL 문을 어디에 저장해야합니까? (0) | 2020.08.18 |
Java Character 클래스에서 빈 문자를 나타내는 방법 (0) | 2020.08.17 |
Android : 모든 기기를 지원하는 배경 이미지 크기 (픽셀) (0) | 2020.08.17 |