Consider the following code which uses "template template" parameters to instantiate a class template using multiple types:
#include <iostream>
using namespace std;
enum E
{
a = 0,
b = 1
};
template <template <E> class Action, class T>
void do_something(const T& value)
{
typedef Action<a> type1;
typedef Action<b> type2;
}
template <E e, class Enable = void>
class Foo
{
};
int main()
{
do_something<Foo>(int(55));
}
Using an older compiler (GCC 4.1.2), the above code compiles fine. However, using a newer compiler (GCC 4.4.6 or 4.8.1), the following error is produced:
test3.cpp:25:27: error: no matching function for call to ‘do_something(int)’
do_something<Foo>(int(55));
So it looks like GCC can't bind to do_something, because the template template parameters only declare a single parameter (an Enum), but Foo actually takes two template parameters (even though one is default.) I guess GCC 4.1.2 allowed the default parameter to be ignored.
Okay, so if I change the template definition to:
template <template <E, class> class Action, class T>
void do_something(const T& value)
{
typedef Action<a> type1;
typedef Action<b> type2;
}
...then no version of GCC I tested will compile it. They all produce a similar error:
test3.cpp:13: error: wrong number of template arguments (1, should be 2)
test3.cpp:10: error: provided for ‘template<E <anonymous>, class> class Action’
So now, the compiler complains because the expression typedef Action<a> type1 only provides a single template parameter. Apparently, I'm not able to implicitly use the default parameter here.
Is there some way I can use the default parameter of a template in a template template function?
Default arguments are ignored for parameters of template arguments. There's this example in n3337, chapter [temp.arg.template], paragraph 2:
template<class T> class A { /∗ ... ∗/ };
template<class T, class U = T> class B { /∗ ... ∗/ };
template <class ... Types> class C { /∗ ... ∗/ };
template<template<class> class P> class X { /∗ ... ∗/ };
template<template<class ...> class Q> class Y { /∗ ... ∗/ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
Note the comment at X<B> xb; above. I can't find the normative text, I'm afraid.
You can correlate this with functions - default arguments are not a part of a signature, either. The same thing would also happen if you tried to call a function that has a parameter defaulted through a function pointer.
With a using template alias, a new feature in C++11, you can create a one-parameter template that is equivalent to another template that has two parameters, one of which is defaulted.
template <E e> using Foo1 = Foo<e>;
This creates Foo1, a one-parameter template, even though Foo technically has two arguments, one of which is defaulted. You can use it as:
do_something<Foo1>(int(55));
Alternatively, if C++11 features such as using are not available, then you scan specify the default in your declaration of do_something. This means then, unfortunately, that do_something can no longer deal with simple one-arg templates. Hence, I think the using method above is better.
template <template <E, class = void> class Action, class T>
void do_something(const T& value);
If you take this approach, putting the default in the args to do_something, then this default takes precedence over the default specified at the declaration Foo. This is based on my experiments, and I can't comment with confidence on what is, and is not, standard. But I do think the using trick is fully standards-compliant regarding C++11.
(Ubuntu clang version 3.0-6ubuntu3)
Related
I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:
../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^
Both compiler options were set to -std=c++14
template<typename T>
struct ConcreteData
{
T data;
};
template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};
template<typename T, template<typename U> class FieldData>
struct SomeFunc
{
};
template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}
template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;
template<typename T>
using FieldData = FieldDataType<T>;
using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang
};
template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;
template<typename U>
using DataAlias = typename Traits::template FieldData<U>;
void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang
Field dummyField;
freeFunc(new Func, dummyField);
}
};
int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();
return 0;
}
According to cppreference.com:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
and
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?
Borrowing the minimal example of #Oktalist.
template <typename>
class T {};
template <typename _>
using U = T<_>;
template <template <typename> class X>
void f(A<X>, A<X>) {}
if you replace f by:
template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.
The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
Using this rule and the rules for equivalence [temp.type]/1:
T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).
This is the same scenario for the alias FieldData and the class template ConcreteData.
There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.
I have the following reasonably simple function template:
template <class OrderedSetType, template<class> class SupersetType>
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
{
return OrderedSetType();
}
It's called like this:
f(std::vector<std::string>());
And the compiler fails to deduce the template parameter. The diagnostic message isn't particularly helpful:
<source>: In function 'int main()':
<source>:12:33: error: no matching function for call to 'f(std::vector<std::__cxx11::basic_string<char> >)'
f(std::vector<std::string>());
^
<source>:5:16: note: candidate: template<class OrderedSetType, template<class> class SupersetType> OrderedSetType f(const SupersetType<OrderedSetType>&)
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
^
<source>:5:16: note: template argument deduction/substitution failed:
<source>:12:33: note: template parameters of a template template argument are inconsistent with other deduced template arguments
f(std::vector<std::string>());
^
Why does the error occur? Happens with GCC 7.3 with -std=c++14, does not happen with -std=c++17. Which changes in the C++ 17 standard allowed for this code to compile? And can I make it compile for C++14?
Here's the live demo: https://godbolt.org/g/89BTzz
Specifying the template arguments explicitly doesn't help, by the way.
P. S. In the meantime, MSVC has no problems with this piece of code, but clang 5 and 6 cannot compile it even in C++17 mode. So either clang has a bug and fails to compile standard-compliant code, or GCC has a bug and successfully compiles code that it shouldn't (with -std=c++17).
Which changes in the C++ 17 standard allowed for this code to compile?
You're declaring the template template parameter SupersetType contaning only one template parameter, but the template template argument std::vector<std::string> has two, i.e. std::string and the default template argument std::allocator<string>. Before C++17 they don't match and leads to error (then you have to make them match to solve the issue), since C++17 (CWG 150) it's allowed; i.e. the default template arguments are allowed for a template template argument to match a template template parameter with fewer template parameters.
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ...Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++17 after CWG 150
// Error earlier: not an exact match
X<C> xc; // OK in C++17 after CWG 150
// Error earlier: not an exact match
Try with
template <template <typename...> class SupersetType,
typename FirstT, typename ... OthersTs>
FirstT f (SupersetType<FirstT, OthersTs...> const & superset)
{ return FirstT{}; }
or also
template <template <typename...> class SupersetType, typename FirstT>
FirstT f (SupersetType<FirstT> const & superset)
{ return FirstT{}; }
The problem is that std::vector doesn't accept only a type but two; the second is an allocator with a default value.
So you have to take in count this problem.
Obviously you can write f() with a template-template parameter that accept only two types
template <template <typename, typename> class SupersetType,
typename FirstT, typename SecondT>
FirstT f (SupersetType<FirstT, SecondT> const & superset)
{ return FirstT{}; }
but if you use a template parameter that accept a variadic list of types, you have a more flexible f() (that match more containers)
While this doesn't provide an answer to your problem, it provide an alternative.
Remember that all standard container have a public type named value_type. That means you could easily skip the template template and only have something like
template<typename ContainerT>
typename ContainerT::value_type f(ContainerT const& superset)
{
return typename ContainerT::value_type();
}
As long as your SupersetType follows the standard containers with a value_type member, it should work.
While exploring this answer I discovered that a template that takes a parameter pack will not be accepted by a template that expects template that has a specific number of parameters.
This seems to me that it is a defect since if a template can take any number of parameters, it should be able to map to a specific number. Is there a language lawyer that could explain why this is not allowed?
Here is a simple example:
template <typename...Ts>
using pack = void;
template <template <typename> class>
using accept_template = int;
accept_template<pack> value = 0;
I wouldn't use it in this exact scenario of course. It would be used to pass a template to another template which would use the passed template in some manner. In my answer that I linked, I have stated a workaround, but I still feel that this is a defect.
This restriction was loosened as a result of P0522, which introduces new rules to handle how template template-arguments match template template-parameters. As a result, from the paper:
template<class T, class U = T> class B { /* ... */ };
template <class ... Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<B> xb; // OK, was ill-formed:
// default arguments for the parameters of a template argument are ignored
X<C> xc; // OK, was ill-formed:
// a template parameter pack does not match a template parameter
Your example fails to compile in C++14, but will compile in C++17.
I wasted countless hours to pinpoint an issue with gcc. I wanted to test our code base with another compiler to look for more warnings that Clang might have missed. I was shocked that practically half of the project stopped to compile due to failure of template argument deduction. Here I've tried to dumb my case down to the simplest piece of code.
#include <type_traits>
struct Foo
{ };
// This is a template function declaration, where second template argument declared without a default
template <typename T, typename>
void foo(const Foo & foo, T t);
// This is a template function definition; second template argument now has a default declared
template <typename T, typename = typename std::enable_if<1>::type>
void foo(const Foo & foo, T t)
{
}
int main(int argc, char ** argv)
{
foo(Foo{}, 1);
return 0;
}
Ignore a 1 in the std::enable_if<1>. Obviously it's a constant value just to not complicate things when it does not matter.
This piece of code compiles[1] with clang (3.4 through 4.0), icc (16, 17), Visual C++ (19.00.23506). Basically, I couldn't find any other c++11 compiler that, except gcc (4.8 through 7.1), does not compile this piece of code.
The question is, who's right and who's wrong here? Does gcc behave according to the standard?
Obviously this is not a critical issue. I can easily move std::enable_if to the declaration. The only victim would be aesthetics. But it is nice to be able to hide an ugly 100 characters long std::enable_if piece of code, that is not immediately relevant for the user of the library function, in the implementation.
Live example on godbolt.org.
What the standard says ([1] page 350):
The set of default template-arguments available for use with a
template declaration or definition is obtained by merging the default
arguments from the definition (if in scope) and all declarations in
scope in the same way default function arguments are (8.3.6). [
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 ]
So GCC is wrong here. It ignores default template arguments in declarations.
Not all declarations, only function template declarations. Class template declarations are okay:
#include <type_traits>
template <typename T, typename>
struct Foo;
template <typename T, typename = typename std::enable_if<1>::type>
struct Foo
{
T t;
};
int main()
{
Foo<int> foo;
return 0;
}
Live example on godbolt.org
Probably it is due to the nature of how non-default arguments are deduced. In the function template they are deducted from function arguments. In the class template we have to specify them explicitly.
Anyway, I have created a bug report.
My problem can be resumed by the following piece of code:
template <typename T> struct C2;
template <typename T>
struct C1
{
template <typename Type,
template <typename Ti> class Container = C2>
void m() {}
};
template <typename T>
struct C2
{
template <typename Type = int,
template <typename Ti> class Container = C2> // <-- Here is the problem!
void m() {}
};
The gnu compiler, version 4.8.1 fails with the following message:
test-temp.C:16:47: error: invalid use of type ‘C2<T>’ as a default value for a template template-parameter
template <typename Ti> class Container = C2>
It refers to default template parameter C2 for the the method C2::m.
Apparently (it is my opinion), the compiler is seeing C2<T> as default parameter instead of C2 (without <T>). So, when it finds the instruction it fails because type C2<T> does not match with Container.
However, clang++, just for exactly the same code, compiles fine!
My questions:
Which compiler has the truth?
Is there some alternative for expressing the same sense with the current version of gnu compiler?
Thanks in advance
Leandro
I think Clang is correct, and g++ is in error, quote from the draft Standard (bold emphasis is mine)
14.6.1 Locally declared names [temp.local]
1 Like normal (non-template) classes, class templates have an
injected-class-name (Clause 9). The injectedclass-name can be used as
a template-name or a type-name. When it is used with a
template-argument-list, as a template-argument for a template
template-parameter, or as the final identifier in the
elaborated-typespecifier of a friend class template declaration, it
refers to the class template itself. Otherwise, it is equivalent to
the template-name followed by the template-parameters of the class
template enclosed in <>.
You can use the :: scope resolution operator to beat g++ into submission
template <typename T>
struct C2
{
template <typename Type = int,
template <typename Ti> class Container = ::C2>
// ^^ <-- here is the solution!
void m() {}
};
Live Example.
So does the 14.6.1 reference in TemplateRex's answer mean that G++ is correct (and Clang and VC++ are wrong) to accept this, since it uses X as the template argument to a template template parameter?
template< template< typename > class T >
class factory { };
template< typename T >
class X
{
friend class factory< X >; // ***
};
int main()
{
}
In this example G++ treats X as the name of the class template, whereas Clang and VC++ treat it as the injected class name.
Edit: Clang 5.0.0 now accepts the code, the same as G++ and EDG.