Amazed (and cofused) by a similar question I tried myself the example that the mentioned question found in the standard:
template <typename T, typename U = int> struct S;
template <typename T = int, typename U> struct S
{ void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
int main()
{
S s; s.f();
return 0;
}
The code above prints void S<int, int>::f() [T = int, U = int]
compiled with gcc HEAD 8.0.1 201803 but fails to compile with clang HEAD 7.0.0 unless angle brackets are used during instantiation:
S s; s.f(); // error: declaration of variable 's' with deduced type 'S' requires an initializer
S<> t; t.f(); // Correct
This issue aside, I've checked the other template flavors for this particular behavior and the code is accepted or rejected in a pretty irregular manner:
Template function
template <typename T, typename U = int> void function();
template <typename T = int, typename U> void function()
{ std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main()
{
/* Rejected by GCC: no matching function for call to 'function()'
template argument deduction/substitution failed:
couldn't deduce template parameter 'T'
same error with function<>()
CLang compiles without issues */
function(); // CLang prints 'void function() [T = int, U = int]'
return 0;
}
Template variable
template <typename T, typename U = int> int variable;
template <typename T = int, typename U> int variable = 0;
int main()
{
/* GCC complains about wrong number of template arguments (0, should be at least 1)
while CLang complains about redefinition of 'variable' */
std::cout << variable<> << '\n';
return 0;
}
Template alias
template <typename T, typename U = int> using alias = int;
template <typename T = int, typename U> using alias = int;
int main()
{
/* GCC complains about redefinition of 'alias'
while CLang compiles just fine. */
alias<> v = 0;
std::cout << v << '\n';
return 0;
}
The standards text about this feature doesn't tell apart the different template types so I thought that they should behave the same.
But yet, the template variable case is the one rejected by both compilers, so I have some doubts about the template variable option. It makes sense to me that CLang is right rejecting the template variable complaining about redefinition while GCC is wrong by rejecting the code for the wrong reasons, but this reasoning doesn't follow what the standard says in [temp.param]/10.
So what should I expect for the case of template variables?:
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.
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.
In C++ this can be achieved using template parameters. 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 class template argument deduction, this is a clang bug. From [temp.param]/14:
The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are ([dcl.fct.default]). [ Example:
template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
When you write S s
, both template arguments are defaulted, and so the rewrite of the default constructor is:
template <typename T=int, typename U=int>
S<T, U> __f();
Which is viable and should deduce S<int, int>
.
For functions, you do not need to specify <>
to call a function template if all the template parameters can be deduced. This is [temp.arg.explicit]/3:
If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list
<>
itself may also be omitted.
Note that this applies to deduction. There is no deduction for alias templates or variable templates. Hence, you cannot omit the <>
. This is [temp.arg]/4:
When template argument packs or default template-arguments are used, a template-argument list can be empty. In that case the empty
<>
brackets shall still be used as the template-argument-list.
Disclaimer: the following is valid in context of C++14. With C++17 both compilers are wrong. See the another answer by Barry.
Looking into details I see that Clang is correct here, while GCC is confused.
First case, class template (unlike function template) indeed requires <>
.
Second case, function template, is treated by Clang exactly like the first case, without the syntactic requirement to use <>
to indicate that template is used. This applies in C++ to function templates in all contexts.
Third case: as of variables, I see that Clang is correct while GCC is confused. If you redeclare the variable with extern
Clang accepts it.
template <typename T, typename U = int> int variable = 0;
template <typename T = int, typename U> extern int variable;
int main()
{
// accepted by clang++-3.9 -std=c++14
std::cout << variable<> << '\n';
return 0;
}
so it again behaves consistently both with standard and the previous cases. Without extern
this is redefinition and it is forbidden.
Fourth case, using
template. Clang again behaves consistently. I used typeid to ensure that alias is indeed int:
template <typename T, typename U = int> using alias = int;
template <typename T = int, typename U> using alias = int;
int main()
{
alias<> v = 0;
std::cout << v << '\n';
std::cout << typeid(v).name() << '\n';
return 0;
}
then
$ ./a.out | c++filt -t
outputs
0
int
So Clang indeed makes no difference of what kind of template is re-declared, as stated in the standard.
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