gcc 11.2 can't seem to compile this:
template <typename T = int>
struct Test {};
template <typename T> void foo(T& bar) {}
int main()
{
Test t;
foo<Test>(t);
}
but has no problem with
template <typename T = int>
struct Test {};
template <typename T> void foo(T& bar) {}
int main()
{
Test t;
foo<Test<>>(t);
}
Is this a compiler bug?
This question seems to suggest it should work.
CodePudding user response:
GCC is right. An empty template argument list, <>
for a function template is allowed to be omitted ([temp.arg.explicit]/4). In other cases, the template argument list is generally required in order to name a particular specialization of a template, even if it is empty. See the grammar for simple-template-id, [temp.names]/1.
As a limited exception to the rule, if the name of a class template without a template argument list appears in a context where a concrete type is required, it is known as a "placeholder for a deduced class type" and this is only allowed in specific contexts listed in [dcl.type.class.deduct]. The most common one is a variable declaration like std::pair p("foo", 1)
where the compiler will deduce std::pair<const char*, int>
in C 17 and later.
In your code, you are trying to refer to a particular specialization of class template Test
, without specifying the template argument list, and not in a context where the template arguments can be deduced. Therefore, it's not allowed.
CodePudding user response:
The new Class template argument deduction (CTAD) (since C 17) is applied to declarations. The expression foo<Test<>>(t);
is not a declaration, it is a template function call.
CodePudding user response:
In your first code snippet, you specify a class template (Test
) as a template parameter to foo
and not a type (like an instance of a class template). You would need a function that takes a template template parameter to handle that.
Example:
#include <type_traits>
template<template<class> class T, class U> // T = Test, U = deduced to int
void foo(T<U>& bar) {
std::cout << std::is_same<T<U>, Test<int>>::value << '\n'; // true
}
int main() {
Test t;
foo<Test>(t); // now ok
}
In your second snippet, foo<Test<>>(t);
instantiates foo<Test<int>>(Test<int>&);
since <>
makes the template instantiation use the default type for the template parameter, which is int
.