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.
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.
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.
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
Sometimes I see syntax like this.
template<typename T,typename = int>
int foo(){
//...
}
what part typename = int mean?
Where it can be used?
foo has two template arguments. The first is called T and the second is unnamed and defaults to int.
In your piece of code alone there is no reason to use the second argument. Unnamed template arguments often come up with SFINAE. An example from cppreference:
// primary template handles non-referenceable types:
template<class T, class = void>
struct reference_traits {
using add_lref = T;
using add_rref = T;
};
// specialization recognizes referenceable types:
template<class T>
struct reference_traits<T, std::void_t<T&>> {
using add_lref = T&;
using add_rref = T&&;
};
template<class T>
using add_lvalue_reference_t = typename reference_traits<T>::add_lref;
template<class T>
using add_rvalue_reference_t = typename reference_traits<T>::add_rref;
The only reason for the primary template to have a second argument is that it can be specialized. When possible the more specialized specialization is instantiatied. If this fails (because T& is not valid) then "substitution failure is not an error" (SFINAE) kicks in and the primary template is instantiated instead.
A simpler example of unnamed argument is when you want a template argument merely as a tag to distinguish different instantiations:
template<typename = int>
struct bar {
// ...
};
Even if the implementation of bar does not depend on the template argument you might want to have bar<double> and bar<std::string> be two distinct types.
this is rarely used ...
but this is the default value for the typename but you don't need it here because the compiler itself can overload the function automatically and get the right type for the right arguments you passed !
also it type for what typename ? it's not makes sense here !
it used when you are using nested template ...
I found out in the original reference for C++ :
The template parameter lists of template template parameters can have
their own default arguments, which are only in effect where the
template template parameter itself is in scope:
// class template, with a type template parameter with a default
template<typename T = float> struct B {};
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T> struct A
{
void f();
void g();
};
// out-of-body member function template definitions
template<template<typename TT> class T>
void A<T>::f()
{
T<> t; // error: TT has no default in scope
}
template<template<typename TT = char> class T>
void A<T>::g()
{
T<> t; // ok: t is T<char>
}
this is the link
This code compiles just fine:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename T>
void NonVariadicFunc(Struct<T>);
int main() {
NonVariadicFunc<int>(ConvertsToStruct{});
return 0;
}
But an attempt to make it a little more generic, by using variadic templates, fails to compile:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename... T>
void VariadicFunc(Struct<T...>);
int main() {
VariadicFunc<int>(ConvertsToStruct{});
return 0;
}
What's going wrong? Why isn't my attempt to explicitly specify VariadicFunc's template type succeeding?
Godbolt link => https://godbolt.org/g/kq9d7L
There are 2 reasons to explain why this code can't compile.
The first is, the template parameter of a template function can be partially specified:
template<class U, class V> void foo(V v) {}
int main() {
foo<double>(12);
}
This code works, because you specify the first template parameter U and let the compiler determine the second parameter. For the same reason, your VariadicFunc<int>(ConvertsToStruct{}); also requires template argument deduction. Here is a similar example, it compiles:
template<class... U> void bar(U... u) {}
int main() {
bar<int>(12.0, 13.4f);
}
Now we know compiler needs to do deduction for your code, then comes the second part: compiler processes different stages in a fixed order:
cppreference
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.
Implicit conversion takes place at overload resolution, after template argument deduction. Thus in your case, the existence of a user-defined conversion operator has no effect when compiler is doing template argument deduction. Obviously ConvertsToStruct itself cannot match anything, thus deduction failed and the code can't compile.
The problem is that with
VariadicFunc<int>(ConvertsToStruct{});
you fix only the first template parameter in the list T....
And the compiler doesn't know how to deduce the remaining.
Even weirder, I can take the address of the function, and then it works
It's because with (&VariadicFunc<int>) you ask for the pointer of the function (without asking the compiler to deduce the types from the argument) so the <int> part fix all template parameters.
When you pass the ConvertToStruct{} part
(&VariadicFunc<int>)(ConvertToStruct{});
the compiler know that T... is int and look if can obtain a Struct<int> from a ConvertToStruct and find the apposite conversion operator.
In C++, Explicit specializations of function templates is like:
template<typename T> return_type fun_name();
template<> return_type fun_name<int>(){/* blabla */}
The <int> in the above example is called template argument. Sometimes <int> can be ommitted because compiler can do Template Argument Deduction
But I can't find out why Template Argument Deduction failed in the following example:
//-------------failed case-------------
template <typename T>
struct deduce{
typedef T* type;
};
template <typename T>
typename deduce<T>::type fun1();
template <>
typename deduce<float>::type fun1<float>() //error if no "<float>" after fun1
{
}
//------------now the "Template Argument Deduction" works------------
template <typename T>
struct some_struct{
T* p;
};
template <typename T>
some_struct<T> fun2();
template <>
some_struct<float> fun2() // no error even if no "<float>" after fun2
{
}
If no <float> is after fun1, The error message is:
error: template-id ‘fun1<>’ for ‘float* fun1()’ does not match any template declaration
Maybe the compiler think the type(deduce<float>::type) marked by typename is less reliable than normal types ?
Let me provide an example of why non-deduced contexts are non-deduced. Template deduction is basically trying to match on the input. If I had:
template <class T> void foo(T );
and I call foo(4), that's easy. T=int. If I call foo('x'), T=char. These are easy substitutions to make. If T is nested somewhere in the type, like:
template <class T> void bar(std::vector<T> );
that's still totally doable. If I call it with a std::vector<std::vector<float>>, T=std::vector<float>. Still no problem.
Now consider this one:
template <class T> void baz(typename X<T>::type );
baz(4);
What's T? In all our previous cases, there was one obvious choice for T that was deduced directly from the argument passed to the function template. But here, that's not the case. We have an extra layer of indirection - we need to deduce a T to make a type X<T> whose member typedef type is int. How do we find such a thing?
Now let's say we had this:
template <class T> struct X { using type = T; };
Ok now it's easy right? T=int? Well, not so fast. For the primary template, that would work in this case. But what if there was also this specialization:
template <class T> struct X<T*> { using type = T; };
(that is, X is std::remove_pointer). Now we're in a situation where T=int works... but T=int* also works. And maybe there's some other type out there that also works for int. How do you pick the right one?
This problem - picking a template parameter in the nested-name specifier of qualified-id - is really hard and has no obvious path forward. So the compiler just won't take a path forward. It's a non-deduced context. T will never be deduced in the call to baz, the caller has to provide it:
baz<int>(4); // ahhhhh, ok, you wanted X<int>::type
Back to your question. some_struct<T> is a deduced-context, but typename deduce<T>::type is a non-deduced context. I hope it's clear now why the former works but the latter doesn't.
Maybe the compiler think the type(deduce<float>::type) marked by typename is less reliable than normal types ?
It has nothing to do with typename, the point is that deduce<T>::... is a nested-name-specifier; which belongs to Non-deduced contexts:
(emphasis mine)
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
So, for
template <>
typename deduce<float>::type fun1()
deduce<float>::type (i.e. float*) will be used to deduce type T for deduce<T>::type, but T won't be deduced, template argument deduction fails. You have to explicitly specify it as float.
Consider this example (also available on wandbox):
template <template <auto> class>
void test() { }
template <int>
struct X { };
Trying to instantiate test<X>() on clang++ 4.0 (trunk) results in a compilation error:
error: no matching function for call to 'test'
test<X>();
^~~~~~~
note: candidate template ignored:
invalid explicitly-specified argument for 1st template parameter
void test() { }
My initial assumption/intuition was that test could be used to match any template having a non-type parameter.
However, the following code snippet successfully compiles:
template <template <auto> class>
void test() { }
// vvvv
template <auto>
struct X { };
Is this intended? Could not find anything conclusive in P0127R2.
It's definitely intended. Template-template parameters can only match templates which take the same kinds of arguments. This:
template <template <auto> class>
void test() { }
can only be instantiated with a class template that can take any kind of non-type parameter. But this:
template <int>
struct X { };
is not such a class template. X can only be instantiated with an int. It simply does not match the specification for the template template parameter, hence the error. What if test wanted to instantiate its class template with a pointer type? Or pointer to function or pointer to member? That would be impossible.
Your second attempt, with template <auto> struct X { }; does match the template-template parameter, hence is well-formed. Note also that the reverse, having test take a template <int> class parameter and passing in template <auto> struct X { }; is also well-formed as the argument is more general than the parameter.
The relevant wording is in [temp.arg.template]:
A template-argument matches a template template-parameter P when each of the template parameters in the template-parameter-list of the template-argument’s corresponding class template or alias template A matches
the corresponding template parameter in the template-parameter-list of P. Two template parameters match if they are of the same kind (type, non-type, template), for non-type template-parameters, their types are
equivalent (14.5.6.1), and for template template-parameters, each of their corresponding template-parameters matches, recursively.
Note: the equivalence wording accepts the auto - auto case and rejects the auto - int case, but also seems to reject the int - auto case (based on my reading). I'm going to try to get some clarification on it.
In addition to Barry's answer, which made me curious, here are the four possible combinations and results using Clang 4.0 (SVN), see also on wandbox:
template <bool> struct obj_bool { }; // object taking a value of boolean type
template <auto> struct obj_auto { }; // object taking a value of deduced type
// ^^^^^^ Note: this is a template value argument (non-type template argument)
template <template <auto> typename> void fn_auto() { }
template <template <bool> typename> void fn_bool() { }
// ^^^^^^^^^^^^^^^^^^^^^^^^ Note: this is a template type argument
// ^^^^^^ taking a template value argument
int main() {
fn_bool<obj_bool>(); // #1 bool->bool OK (exact match)
fn_auto<obj_auto>(); // #2 auto->auto OK (exact match)
fn_bool<obj_auto>(); // #3 bool->auto OK (sub-set)
//fn_auto<obj_bool>(); // #4 auto->bool Error: no matching function.
}
From that, #1 and #2 are obviously exact matches and are working as expected. #3 would invoke the bool implementation on a template that can handle not only bool but all types, whereas #4 would try to invoke a definition expecting a generalized object (auto) with an object providing only a sub-set (bool) of possibilities.
The templated function fn_auto promises possible instantiations for templates taking any value type (auto). Thus giving it only a sub-set of possibilities (bool) violates this promise.
Though not immediately obvious, the restriction makes sense. And sorry for my wording not being C++ Standard compliant.
Let me present my problem with an example :
template <typename T> class a{
public:
T data;
a():data(T()){}
a(T temp): data(temp) {}
};
So if write in main() like
a(30);
a("String");
So according to the template argument deduction rule , it should be able to generate the first temporary class as a<int>(30) etc
But I the error which says:
missing template arguments before '(' token
so why this happens, this is only true for function template?
Template parameter deduction from arguments only works for functions, never for classes. Until you know the type of the class, i.e. all its template parameters, you don't even know which member functions the class has!
So, you always have to say the template parameters if you want to construct an object directly:
a<int> x(30);
Here's a little thought experiment to expand on the above. Suppose we have
template <typename T> class Foo;
and we are calling Foo::somefunction(x);, where x is some type. You think, well, I declared somefunction() like this:
template <typename T> class Foo
{
static void somefunction(const T & x);
};
so it should be obvious that T is the same type as the type of x. But now imagine I have a specialization:
template <> class Foo<char>
{
static void anotherfunction(double x);
};
The class Foo<char> doesn't even have a function somefunction(), so the expression Foo::somefunction(x) doesn't even get to the stage where I could look up the argument!
The usual way around this is to make a free helper function that constructs your object:
template <typename T> a<T> make_a(const T & x) { return a<T>(x); }
Since this is a function template, its parameters can be deduced:
make_a(30); // type a<int>
make_a("hello"); // type a<char[6]>
The constructor is not a template, its the class which is a template. So when you write a(30), the template argument deduction for the class template cannot be done!
If there exists a constructor template, then the template argument for the templated constructor can be deduced by the compiler. For example here:
template <typename T> class A{
public:
template<typename U>
A(const U &): {} //note : it's a constructor template
};
A<char> obj(30); //U is deduced as int
In the above example, only U can be deduced, you still have to provide T. Its because
U is a template argument for the constructor template. Template argument deduction can be done in this case.
T is a template argument for the class template. Template argument deduction cannot be done here.
You still need to declare a temporary as, for example, a<int>(30).
You cannot infer class template arguments from the arguments to the constructor- unfortunately.
Template type deduction only happens for template functions. You need to specify the parameters for a template class instantiation . You can use a function template to deduce the template parameter and return the appropriate type. In c++0 x you could use auto to hold the instance. Can't easily write example code for you on my phone!