I have this code:
struct Base {};
template<typename T>
struct Foo : Base {};
struct Bar {
template<typename T> // v--- What's happening here?
Bar(T foo) : baz{std::make_unique<Foo>(foo)} {}
std::unique_ptr<Base> baz;
};
As a surprise, GCC and Clang accept and compiles it. It seem to deduce the template parameter of Foo
, but it wouldn't make sense. How come the compilers accept that even if there is no overload of std::make_unique
that takes a template template parameter? Live example
You cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.
In UML models, template parameters are formal parameters that once bound to actual values, called template arguments, make templates usable model elements. You can use template parameters to create general definitions of particular types of template.
There are some situations where a template is always invalid, no matter what template arguments are supplied, but the compiler isn't able to figure that out, because it doesn't have the ability to try substituting every possible set of template arguments. According to the standard ([temp.res]/8):
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.
That means that the compiler is allowed to be smart and prove that there is no valid specialization, and produce a compilation error, but it's also allowed to not be smart enough, and not produce a compilation error. Of course, once the template is instantiated, then the compiler must diagnose the error.
It's not illegal to use the name of a template without template arguments. There are some circumstances where the compiler can deduce arguments. For example:
template <class T>
void foo(T x);
int main() {
void (*p)(int) = foo; // p points to foo<int>
}
In your code, it turns out that you've used Foo
in a context where the template arguments can't be deduced. If the compilers were smarter, they would have figured that out. But the fact that they didn't manage to figure it out does not mean your code is correct.
The reason this works is because C++ doesn't actually create template functions until they're called from somewhere in the code. In this instance, since your code doesn't ever try to create a Bar
, your template one-arg Bar
constructor is never generated, so the compiler never needs to check if the code is correct.
Template functions are generated by the compiler as soon as they're used, and they're generated only based on the types passed into them. So, if you try to create a Bar
like so:
Foo<int> f;
Bar b = Bar(f);
The compiler will try to generate the constructor and fail with an error like:
error C2955: 'Foo' : use of class template requires template argument list
Because it now knows the code is wrong.
If you think about it, this is how it has to work: since C++ doesn't allow you to put constraints on template types, the compiler would have to try every possible combination of template types to figure out if a template function has syntax errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With