Home > OS >  Defaulted Template in Template Function Requires Empty Angle Brackets <>
Defaulted Template in Template Function Requires Empty Angle Brackets <>

Time:03-31

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.

  • Related