This question already has answers here:
Why not infer template parameter from constructor?
(12 answers)
Closed 7 years ago.
template< class T >
class Foo {
public:
Foo( T t ) { }
};
int main () {
int i = 0;
Foo f( i );
}
In the above code, the compiler complains that template arguments are missing before 'f'. I understand that deducing template arguments for a class from the arguments to the constructor is not part of the standard, but my question is why? Doesn't the compiler have all the information it needs to implicitly instantiate Foo<int> and call its constructor?
Edited to make it clear that I'm calling the constructor with an int (as opposed to a short, long, void*, etc.)
Because nobody has specified how exactly that works. There is a current proposal to the standard committee to make it work. It also lists some of the difficulties:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4471.html
Update: Here's the newest version of the proposal:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0091r0.html
TL;DR: Template specialization
They can, but only template arguments on the function itself, not on the type.
The declaration Foo f(0); is illegal, because there is no type named Foo. Perhaps you were thinking of
auto f = Foo(0);
but that is not allowed either, because the compiler doesn't know what scope to search in (there are infinite potential types with a constructor named Foo and, with specialization, possibly more than one with a constructor Foo(int))
The usual method to do this is with a factory helper function:
auto f = make_foo(0);
where the factory function's return type depends on type deduction of its parameters.
You can imagine that the factory functions could be automatically generated in namespace scope and then the usual function overloading rules applied, but this runs into significant difficulty because there can be template arguments on both the type and the constructor itself. These could simply be concatenated, with the limitation that this would exclude class templates with variadic argument lists, because there would be no way to distinguish where the type parameters end and the function parameters begin.
Foo is a class template, not a class. Its type always needs to be supplied in some way or another for a class to be generated with the correct types. You can't do Foo because Foo isn't a type, but Foo<int> is. It creates a class like this:
class Foo {
public:
Foo( int t ) { }
};
If you only supplied Foo, the compiler wouldn't know how to generate the class. Foo<int> f(0) works because Foo<int> generates the class, substituting T with int. By the type you call the constructor, the compiler already knows that the constructor is accepting an int.
The name of the class you want to instantiate is
Foo<int> (as you indicate).
It is also possible to write Foo<short> f(0), or Foo<unsigned long> f(0),
or Foo<Foo<double>*> f(0). In those cases, however, you don't expect the compiler to be able to guess the type if you write only Foo f(0).
One could imagine that a C++ could have been specified with rules to make
some such guesses in certain ways (such as, the literal 0 implies type parameter int and no other), but then the language would be even more
complicated than it is now and there would be additional ways for
people to make programming errors.
Actually writing what you mean in a declaration like this
seems like not too much to ask.
Edit: After posting this, I noticed in another answer that there is
a proposal to make such a feature of C++, indeed as one could imagine.
Related
my question today is pretty simple: why can't the compiler infer template parameters from class constructors, much as it can do from function parameters? For example, why couldn't the following code be valid:
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
}
As I say, I understand that this isn't valid, so my question is why isn't it? Would allowing this create any major syntactic holes? Is there an instance where one wouldn't want this functionality (where inferring a type would cause issues)? I'm just trying to understand the logic behind allowing template inference for functions, yet not for suitably-constructed classes.
I think it is not valid because the constructor isn't always the only point of entry of the class (I am talking about copy constructor and operator=). So suppose you are using your class like this :
MyClass m(string s);
MyClass *pm;
*pm = m;
I am not sure if it would be so obvious for the parser to know what template type is the MyClass pm;
Not sure if what I said make sense but feel free to add some comment, that's an interesting question.
C++ 17
It is accepted that C++17 will have type deduction from constructor arguments.
Examples:
std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
Accepted paper.
You can't do what you ask for reasons other people have addressed, but you can do this:
template<typename T>
class Variable {
public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
return Variable<T>(instance);
}
which for all intent and purposes is the same thing you ask for.
If you love encapsulation you can make make_variable a static member function. That's what people call named constructor. So not only does it do what you want, but it's almost called what you want: the compiler is infering the template parameter from the (named) constructor.
NB: any reasonable compiler will optimize away the temporary object when you write something like
auto v = make_variable(instance);
In the enlightened age of 2016, with two new standards under our belt since this question was asked and a new one just around the corner, the crucial thing to know is that compilers supporting the C++17 standard will compile your code as-is.
Template-argument deduction for class templates in C++17
Here (courtesy of an edit by Olzhas Zhumabek of the accepted answer) is the paper detailing the relevant changes to the standard.
Addressing concerns from other answers
The current top-rated answer
This answer points out that "copy constructor and operator=" wouldn't know the correct template specializations.
This is nonsense, because the standard copy-constructor and operator= only exist for a known template type:
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;
Here, as I noted in the comments, there is no reason for MyClass *pm to be a legal declaration with or without the new form of inference: MyClass is not a type (it's a template), so it doesn't make sense to declare a pointer of type MyClass. Here's one possible way to fix the example:
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.
This question already has answers here:
Why not infer template parameter from constructor?
(12 answers)
Closed 7 years ago.
template< class T >
class Foo {
public:
Foo( T t ) { }
};
int main () {
int i = 0;
Foo f( i );
}
In the above code, the compiler complains that template arguments are missing before 'f'. I understand that deducing template arguments for a class from the arguments to the constructor is not part of the standard, but my question is why? Doesn't the compiler have all the information it needs to implicitly instantiate Foo<int> and call its constructor?
Edited to make it clear that I'm calling the constructor with an int (as opposed to a short, long, void*, etc.)
Because nobody has specified how exactly that works. There is a current proposal to the standard committee to make it work. It also lists some of the difficulties:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4471.html
Update: Here's the newest version of the proposal:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0091r0.html
TL;DR: Template specialization
They can, but only template arguments on the function itself, not on the type.
The declaration Foo f(0); is illegal, because there is no type named Foo. Perhaps you were thinking of
auto f = Foo(0);
but that is not allowed either, because the compiler doesn't know what scope to search in (there are infinite potential types with a constructor named Foo and, with specialization, possibly more than one with a constructor Foo(int))
The usual method to do this is with a factory helper function:
auto f = make_foo(0);
where the factory function's return type depends on type deduction of its parameters.
You can imagine that the factory functions could be automatically generated in namespace scope and then the usual function overloading rules applied, but this runs into significant difficulty because there can be template arguments on both the type and the constructor itself. These could simply be concatenated, with the limitation that this would exclude class templates with variadic argument lists, because there would be no way to distinguish where the type parameters end and the function parameters begin.
Foo is a class template, not a class. Its type always needs to be supplied in some way or another for a class to be generated with the correct types. You can't do Foo because Foo isn't a type, but Foo<int> is. It creates a class like this:
class Foo {
public:
Foo( int t ) { }
};
If you only supplied Foo, the compiler wouldn't know how to generate the class. Foo<int> f(0) works because Foo<int> generates the class, substituting T with int. By the type you call the constructor, the compiler already knows that the constructor is accepting an int.
The name of the class you want to instantiate is
Foo<int> (as you indicate).
It is also possible to write Foo<short> f(0), or Foo<unsigned long> f(0),
or Foo<Foo<double>*> f(0). In those cases, however, you don't expect the compiler to be able to guess the type if you write only Foo f(0).
One could imagine that a C++ could have been specified with rules to make
some such guesses in certain ways (such as, the literal 0 implies type parameter int and no other), but then the language would be even more
complicated than it is now and there would be additional ways for
people to make programming errors.
Actually writing what you mean in a declaration like this
seems like not too much to ask.
Edit: After posting this, I noticed in another answer that there is
a proposal to make such a feature of C++, indeed as one could imagine.
my question today is pretty simple: why can't the compiler infer template parameters from class constructors, much as it can do from function parameters? For example, why couldn't the following code be valid:
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
}
As I say, I understand that this isn't valid, so my question is why isn't it? Would allowing this create any major syntactic holes? Is there an instance where one wouldn't want this functionality (where inferring a type would cause issues)? I'm just trying to understand the logic behind allowing template inference for functions, yet not for suitably-constructed classes.
I think it is not valid because the constructor isn't always the only point of entry of the class (I am talking about copy constructor and operator=). So suppose you are using your class like this :
MyClass m(string s);
MyClass *pm;
*pm = m;
I am not sure if it would be so obvious for the parser to know what template type is the MyClass pm;
Not sure if what I said make sense but feel free to add some comment, that's an interesting question.
C++ 17
It is accepted that C++17 will have type deduction from constructor arguments.
Examples:
std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
Accepted paper.
You can't do what you ask for reasons other people have addressed, but you can do this:
template<typename T>
class Variable {
public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
return Variable<T>(instance);
}
which for all intent and purposes is the same thing you ask for.
If you love encapsulation you can make make_variable a static member function. That's what people call named constructor. So not only does it do what you want, but it's almost called what you want: the compiler is infering the template parameter from the (named) constructor.
NB: any reasonable compiler will optimize away the temporary object when you write something like
auto v = make_variable(instance);
In the enlightened age of 2016, with two new standards under our belt since this question was asked and a new one just around the corner, the crucial thing to know is that compilers supporting the C++17 standard will compile your code as-is.
Template-argument deduction for class templates in C++17
Here (courtesy of an edit by Olzhas Zhumabek of the accepted answer) is the paper detailing the relevant changes to the standard.
Addressing concerns from other answers
The current top-rated answer
This answer points out that "copy constructor and operator=" wouldn't know the correct template specializations.
This is nonsense, because the standard copy-constructor and operator= only exist for a known template type:
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;
Here, as I noted in the comments, there is no reason for MyClass *pm to be a legal declaration with or without the new form of inference: MyClass is not a type (it's a template), so it doesn't make sense to declare a pointer of type MyClass. Here's one possible way to fix the example:
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.
I find something annoying in C++ and I don't know if there is a trick to avoid this with no overhead. The problem is the following :
For a template function, we can have :
// Function declaration/definition
template<bool Option = false> void myFunction()
{
std::cout<<"Option = "<<Option<<std::endl;
}
// Then I can use :
myFunction<false>();
myFunction<true>();
myFunction(); // <- NO PROBLEM HERE
Now for a template class :
// Class definition/declaration
template<bool Option = false> class MyClass
{
};
// Then I can use :
myClass<false> x;
myClass<true> y;
myClass z; // <- PROBLEM HERE : only "MyClass<> z;" will compile !
Why is the reason of this behaviour ?
Is there any trick to avoid that ?
For a class with optionnal parameters passed as template, I find this not convenient for the end user : he should be able to use the default implementation as a no-templated class...
Why is the reason of this behaviour ?
It's because functions can be overloaded, and types can't.
When you write a function call, the compiler populates an overload set of all the functions it can find with that name, and then figures out which ones match the argument(s) passed. Now, for this to work cleanly with function templates, it allows the template argument types to be deduced from the parameters. Because type parameter inference is allowed in general, it works for your case even when the parameter is defaulted instead.
Types, however, aren't overloaded. While myFunction<true>() and myFunction<false>() are both related to the extent they'll participate in the same overload set, myClass<true> and myClass<false> are separate and unrelated types. With no equivalent of overloading on type names, there's no motivation to add a special case for implicitly naming a fully-specialized template class. The parameters can never be inferred, so it would amount to special syntax only for the case where they're all defaulted.
Is there any trick to avoid that ?
In general, if you want to get template argument deduction for template classes, you can provide a template function wrapper (this works best with C++11 auto)
template <bool Option=false> class MyClass {};
template <bool Option=false> MyClass<Option> make_my_class() {
return MyClass<Option>();
}
// ...
auto z = make_my_class();
Otherwise, I think using typedef (as per Remy's comment) is the best option.
myClass is a class template, not a class. Only myClass<true>, or myClass<>, is a class.
Similarly, myFunction is a function template, not a function. However, when you're invoking a templated function, the template arguments may be deduced for you, and you don't need to specify template arguments explicitly if they can be deduced. Thus the function call expression myFunction(); is valid, and the first argument is deduced as false. It's just that the deduction happens thanks to the default argument rather than matching against function arguments.
my question today is pretty simple: why can't the compiler infer template parameters from class constructors, much as it can do from function parameters? For example, why couldn't the following code be valid:
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
}
As I say, I understand that this isn't valid, so my question is why isn't it? Would allowing this create any major syntactic holes? Is there an instance where one wouldn't want this functionality (where inferring a type would cause issues)? I'm just trying to understand the logic behind allowing template inference for functions, yet not for suitably-constructed classes.
I think it is not valid because the constructor isn't always the only point of entry of the class (I am talking about copy constructor and operator=). So suppose you are using your class like this :
MyClass m(string s);
MyClass *pm;
*pm = m;
I am not sure if it would be so obvious for the parser to know what template type is the MyClass pm;
Not sure if what I said make sense but feel free to add some comment, that's an interesting question.
C++ 17
It is accepted that C++17 will have type deduction from constructor arguments.
Examples:
std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
Accepted paper.
You can't do what you ask for reasons other people have addressed, but you can do this:
template<typename T>
class Variable {
public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
return Variable<T>(instance);
}
which for all intent and purposes is the same thing you ask for.
If you love encapsulation you can make make_variable a static member function. That's what people call named constructor. So not only does it do what you want, but it's almost called what you want: the compiler is infering the template parameter from the (named) constructor.
NB: any reasonable compiler will optimize away the temporary object when you write something like
auto v = make_variable(instance);
In the enlightened age of 2016, with two new standards under our belt since this question was asked and a new one just around the corner, the crucial thing to know is that compilers supporting the C++17 standard will compile your code as-is.
Template-argument deduction for class templates in C++17
Here (courtesy of an edit by Olzhas Zhumabek of the accepted answer) is the paper detailing the relevant changes to the standard.
Addressing concerns from other answers
The current top-rated answer
This answer points out that "copy constructor and operator=" wouldn't know the correct template specializations.
This is nonsense, because the standard copy-constructor and operator= only exist for a known template type:
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;
Here, as I noted in the comments, there is no reason for MyClass *pm to be a legal declaration with or without the new form of inference: MyClass is not a type (it's a template), so it doesn't make sense to declare a pointer of type MyClass. Here's one possible way to fix the example:
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.