So I have the following bit of code:
template <typename Type>
class Delegate
{
public:
Delegate(Type x)
{
}
};
void Method()
{
}
int main()
{
Delegate d(&Method);
return 0;
}
My question is: why can't the compiler deduce the template type based on what's passed into the constructor? The compile error I get is: Argument list for class template Delegate is missing. I understand that, but I thought type inference could overcome this to allow for cleaner syntax.
Because template parameter deduction only applies to functions. Class templates require parameters explicitly, always.
That's why many templates have a "named constructor" a function that simply constructs a temporary instance, but by virtue of being a function template rather than class template deduces parameters. For example std::make_pair.
C++11 introduced this new meaning of auto that actually allows you to deduce type of variable. So if you have C++11, you can create a "named constructor" for your class, like:
template <typename Type>
Delegate<Type> delegate(Type x) { return Delegate<Type>(x); }
and you can create a variable of deduced type with it like:
auto d = delegate(&Method);
Note, that this deduces d to be exactly the type returned by the initializer (you can have auto & or auto && if you want, but not much beyond that). This is way easier than trying to deduce hypothetical Delegate d(&Method), because that would involve cyclical dependency between deducing the type depending on overload resolution between constructors and the set of viable constructors depending on the deduced type (remember, constructors can be overloaded and types can be partially specialized).
It's for the same reason that this won't work:
// attempt to create a std::vector<std::string> of ten "x" strings:
std::vector v(10, "x");
In fact, it should result in the very same error message.
Use something like this to use type deduction:
template <class Type>
Delegate<Type> MakeDelegate(Type const &x)
{
return Delegate<Type>(x);
}
Or just do as you'd do with a std::vector and declare the type explictly.
By the way, main must return int, and arguments of unknown type (i.e. in templates) should be passed with const&.
Related
I was surprised the following code resulted in a could not deduce template argument for T error:
struct foo
{
template <typename T>
void bar(int a, T b = 0.0f)
{
}
};
int main()
{
foo a;
a.bar(5);
return 0;
}
Calling a.bar<float>(5) fixes the issue. Why can't the compiler deduce the type from the default argument?
In C++03, the specification explicitly prohibits the default argument from being used to deduce a template argument (C++03 §14.8.2/17):
A template type-parameter cannot be deduced from the type of a function default argument.
In C++11, you can provide a default template argument for the function template:
template <typename T = float>
void bar(int a, T b = 0.0f) { }
The default template argument is required, though. If the default template argument is not provided, the default function argument is still not usable for template argument deduction. Specifically, the following applies (C++11 14.8.2.5/5):
The non-deduced contexts are:
...
A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
There would be some technical difficulties in achieving that in general. Remember that default arguments in templates are not instantiated until they are needed. Consider then:
template<typename T, typename U> void f(U p = T::g()); // (A)
template<typename T> T f(long, int = T()); // (B)
int r = f<int>(1);
This is resolved today by performing (among other things) the following steps:
attempt to deduce template parameters for candidates (A) and (B);
this fails for (A), which is therefore eliminated.
perform overload resolution; (B) is selected
form the call, instantiating the default argument
In order to deduce from a default argument, that default argument would have to be itself instantiated before completing the deduction process. That could fail, leading to errors outside the SFINAE context. I.e., a candidate that may be entirely inappropriate for a call could trigger an error.
A good reason might be that
void foo(bar, xyzzy = 0);
is similar to a pair of overloads.
void foo(bar b) { foo(b, 0); }
foo(bar, xyzzy);
Moreover, sometimes it is advantageous to refactor it into such:
void foo(bar b) { /* something other than foo(b, 0); */ }
foo(bar, xyzzy);
Even when written as one, it's still like two functions in one, neither of which is "preferred" in any sense. You're calling the one-argument function; the two-argument one is effectively a different function. The default argument notation just merges them into one.
If overloading were to have the behavior that you are asking for, then for consistency it would have to work in the case when the template is split up into two definitions. That wouldn't make sense because then the deduction would be pulling types from an unrelated function that is not being called! And if it was not implemented, it would mean that overloading different parameter list lengths becomes a "second class citizen" compared to "default-argumenting".
It is good if the difference between overloads and defaulting is completely hidden to the client.
Considering the following small code snippet:
template <typename T>
class A{
public:
A() { };
~A() { };
// ...
template <typename outT = T> operator std::vector<outT>(){ /* implicit/explicit cast to std::vector */ }
};
template <typename T = float>
void myFunc(const std::vector<T>& _arg){
printf("myFunc\n");
}
int main(int argc, char const *argv[]) {
A<int> foo;
myFunc(foo);
}
When trying to compile, I get the error
template argument deduction/substitution failed: [...] ‘A<int>’ is not derived from ‘const std::vector<T>’
While, on the other hand, if myFunc is not a template, it will compile and run just fine. I assume the error is caused by the fact that since myFunc accepts multiple vector types as argument, the compiler does not know to what type it should convert foo to. However, shouldn't the default template values be used as fallback in such case? Is there an alternative way to be able to pass an object of class A to myFunc?
Implicit conversions are not considered when attempting to deduce template parameters. Since A<int> cannot match const std::vector<T>, the myFunc template is not a viable candidate. This remains true even if you explicitly instantiate it beforehand (and even if the conversion operator is not templated).
For overload resolution, implicit conversion are considered, which is why the non-templated version works. The templated version never gets to participate because it is not a viable candidate.
The problem is that template argument deduction does not consider implicit conversions, so when you write myFunc(foo) the compiler is unable to determine a type for T. And that's really the end of the story.
See Template argument deduction on C++ reference.
Walter E. Brown gave a fantastic talk about how function templates work (including why you should call them function templates and not templatized functions) at CppCon2018 which I highly recommend.
See "C++ Function Templates: How Do They Really Work?" on youtube.
I've seen code like this:
#include <iostream>
// member_function_templates.cpp
struct X
{
template <class T> void mf(T* t) {
std::cout << "in mf t is: " << *t << std::endl;
}
};
int main()
{
int i = 5;
X* x = new X();
x->mf(&i); //why can this be called without specifying a type as in: x->mf<int>(&i) ??
}
My question is in the comment there in main. Why can you call:
x->mf(&i);
...without specifying a type? Intuition says it should be called like:
x->mf<int>(&i);
... but apparently does not have to be called like this (the above compiles with gcc 4.7 for me either way - with of without explicitly specifying the template.)
And, if you call it without specifying a type for the template, what will the type of T be (in the template function definition)? (I'm guessing it defaults to the type of whatever it is you pass into the function mf as an argument, but further explanation of how this works would be nice)
That is template type deduction and it applies to all template functions, not just members of a non-templated class. Basically the compiler is able to infer (following a set of rules in the standard) what the type T means from the arguments to the function. When the compiler sees the call to mf(&i);, it knows that i is an int, so the argument is an int* and that means that you want the overload that has T==int (*)
You can still provide a particular set of template arguments if you want to force a particular specialization of the template. If, for example, you want the parameter to the template not be the deduced one, but one that is convertible from it:
template<typename T>
void foo(T arg) {...}
int i = 10;
foo(i); // calls foo<int>(i)
foo<double>(i); // calls foo<double>(static_cast<double>(i))
(*) It is slightly more complicated than this, as there could be potentially multiple T types for which the argument would be valid, but the rules in the language determine what particular T will be deduced. In this case, it is int.
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.
I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}