The following code compiles successfully in g++ 7.2.0 (compilation flags are -std=c++14 -Wall -Wextra -Werror -pedantic-errors), but it fails to compile in clang++ 5.0.0 (with the same flags, -std=c++14 -Wall -Wextra -Werror -pedantic-errors) and vc++ 15.4 (compilation flags are /EHsc /Za /std:c++14 /permissive-):
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = Functor<FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
Which compiler behavior is standard compliant? How such template apply may be changed to be compiled on clang++, too?
clang++ error messages:
5 : <source>:5:15: error: too many template arguments for class template 'Bar'
using type = Functor<FixedArguments..., FreeArguments...>;
^ ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
(void)Foo<apply<Bar, int, char>::type>{};
^
9 : <source>:9:8: note: template is declared here
struct Bar{};
vc++ error messages:
5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled
note: after looking at this, this answer would be correct if Bar were an alias template rather than a class template. The workaround works but for other reasons. See Constructors answer for the correct answer of the OP.
This problem is known as the 'alias flaw' and we had a lot of challenges with it in the implementation of kvasir::mpl. The problem is that Bar takes exactly two parameters but sizeof...(FixedArguments)+sizeof...(FreeArguments) could add up to something besides 2.
The compiler can either try and track the potential arity through all the alias calls and only issue errors when the user actually passes something besides 2 or it can "eagerly" give an error simply by proving that an error could occur.
The work around I have found effective in dealing with this is to make the alias call dependant on the size of the input https://godbolt.org/g/PT4uaE
template<bool>
struct depends{
template<template<typename...> class F, typename...Ts>
using f = F<Ts...>;
};
template<>
struct depends<false>{
template<template<typename...> class F, typename...Ts>
using f = void;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
It should be noted that constraining to exactly two is not needed on all compiler I have tested, one could just as easilty constrain to be sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000 and the compiler will still take it only issuing an error if things actually don'T work out on a concrete call.
I would actually like to improve my mental model of how this works internally in order to come up with faster work arounds, in kvasir::mpl we are currently experimenting with tracking the arity manually behind the scenes in order to eliminate the dependant calls which do slow things down a little.
As #T.C. noted in the comments to the question such code is ill-formed (no diagnostic required).
C++14 standard, section "Name resolution" [temp.res], paragraph 8:
If every valid specialization of a variadic template requires an empty
template parameter pack, the template is ill-formed, no diagnostic
required.
Latest drafts of the C++ standard, section "Name resolution" [temp.res], paragraph 8.3:
...The program is ill-formed, no diagnostic required, if:
...
every valid specialization of a variadic template requires an empty template parameter pack...
Additional information: Core Issue 2067.
In accordance with the standard requirements such simple workaround can be written:
template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
using type = Functor<Arguments...>;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};
Live demo
Update: As #odinthenerd noted in the comments this workaround uses an additional type which leads to a slower compilation of the program.
Related
I'd like to ask whether the following code sample should compile:
#include <iostream>
#include <vector>
#include <typeinfo>
using namespace std;
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
// Visual Studio requires this to be:
// using type = C<double, std::allocator<doble>>
};
int main()
{
std::cout << typeid(convert_container<std::vector>::type).name();
}
The code compiles fine with GCC 4.8.1 and Clang 3.4 but not with Visual Studio 2013. The error I get:
error C2976: 'std::vector' : too few template arguments
c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see declaration of 'std::vector'
c:\users\michał\documents\visual studio 2013\projects\transform\transform.cpp(14) : see reference to class template instantiation 'convert_container<std::vector>' being compiled
What does the standard say about this? Am I required to explicitly state all the parameters (including defaulted ones) when using the template template parameter C or is this just a bug in VC++?
Context: The issue araised from Constructor's answer to my previous question: https://stackoverflow.com/a/23874768/2617356
When searching the archives I've found this question: Default values in templates with template arguments ( C++ ) It's basically about the same problem, the question author states that default parameters for template template parameter "had to be" explicitly stated. However, the asker accepted solution that's not quite applicable in my case. The question was not about what is the standard-conforming behaviour, so I believe this is not a duplicate.
Consider the similar
template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;
This is clearly covered by the standard (14.3.3p3 if you're interested, I won't quote it, as GCC and clang do both implement the rule already), where the use of A as a template argument for B is disallowed because of the non-type template parameter. That rule makes no sense if the instantiation of a template template parameter could make use of the template template argument's default template arguments, so the behaviour of MSVC and Intel is more consistent than that of GCC and clang.
Of course, the reasoning "if this were valid, the standard would have inconsistencies" doesn't actually mean it isn't valid, only that it shouldn't be valid. To actually check what the standard says:
14.1 Template parameters [temp.param]
10 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).
8.3.6 Default arguments [dcl.fct.default]
4 Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.
Although not specifically intended to address this use of default template arguments, I think it does manage to do so. Nikos Athanasiou has already included the part of the standard that says any default template arguments of C do get used:
14.1 Template parameters [temp.param]
14 A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.
Since C's default template arguments are used, std::vector's aren't, and MSVC and Intel seem to be correct here.
And to come up with an example that clearly shows that GCC and clang cannot be considered to conform here:
template <typename = char, typename = short>
struct A { };
template <template <typename = void, typename ...> class T>
struct B {
using type = T<>;
};
Both GCC and clang treat B<A>::type as A<void, short>, taking one default template argument from T, and another from A, even though the standard disallows merging of default arguments (and hence default template arguments) in declarations in different scopes.
A workaround for you, to avoid the need to type out the allocator argument, could be to use a template alias:
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
};
template <typename T>
using vector_default_alloc = std::vector<T>;
int main()
{
std::cout << typeid(convert_container<vector_default_alloc>::type).name();
}
I cannot test on MSVC right now, but Intel accepts it, and I see no reason why this variant would be invalid.
A (seemingly related) quote from the standard 14.1 Template parameters
14 . A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.
[ Example:
template <class T = float> struct B {};
template <template <class TT = float> class T> struct A {
inline void f();
inline void g();
};
template <template <class TT> class T> void A<T>::f() { // (*)
T<> t; // error - TT has no default template argument
}
template <template <class TT = char> class T> void A<T>::g() {
T<> t; // OK - T<char>
}
— end example ]
This is the only verse posing limitations to the use of default template parameters of template template parameters (verse 9,11,12 pose limitations on the definition/specification)
As stressed in the comments, OP's case does not involve a default parameter in convert_container (so the above does not apply explicitly). IMHO there are two ways of interpreting the situation :
using type = C<double> is a type alias for a class template; that class "loses" the right to use default template parameters, since it's passed as a template template parameter and all default arguments (of that TT parameter) lie outside the scope of the "typedefing". Then VS is correct.
By tracking the instantiation process : Say a correct compiler instantiates the struct as so (it's just a type substitution - no actual representation of the actual instantiation process is implied)
struct convert_container
{
using type = vector<double>;
};
then OP's case seems fairly legit (and gcc/clang are correct)
FWIW
This compiles in VS2013
template <template <class...> class C>
using tt = C<double>;
int main()
{
std::cout << typeid(tt<std::vector>).name();
}
So the arguments of default template parameters being non legal to pass to template template parameters seems more and more shaky.
Are multiple class template specialisations valid, when each is distinct only between patterns involving template parameters in non-deduced contexts?
A common example of std::void_t uses it to define a trait which reveals whether a type has a member typedef called "type". Here, a single specialisation is employed. This could be extended to identify say whether a type has either a member typedef called "type1", or one called "type2". The C++1z code below compiles with GCC, but not Clang. Is it legal?
template <class, class = std::void_t<>>
struct has_members : std::false_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};
There is a rule that partial specializations have to be more specialized than the primary template - both of your specializations follow that rule. But there isn't a rule that states that partial specializations can never be ambiguous. It's more that - if instantiation leads to ambiguous specialization, the program is ill-formed. But that ambiguous instantiation has to happen first!
It appears that clang is suffering from CWG 1558 here and is overly eager about substituting in void for std::void_t.
This is CWG 1980 almost exactly:
In an example like
template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
it appears that the second declaration of f is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.
If you use the non-alias implementation of void_t:
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
then clang allows the two different specializations. Sure, instantiating has_members on a type that has both type1 and type2 typedefs errors, but that's expected.
I don't believe it's correct, or at least, not if we instantiate has_members with a type that has both type1 and type2 nested, the result would be two specializations that are
has_members<T, void>
which would not be valid. Until the code is instantiated I think it's ok, but clang is rejecting it early. On g++, your fails with this use-case, once instantiated:
struct X
{
using type1 = int;
using type2 = double;
};
int main() {
has_members<X>::value;
}
The error message is doesn't seem to describe the actual problem, but it at least is emitted:
<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
has_members<X>::value;
^~~~~
If you instantiate it with a type that has only type1 or type2 but not both,
then g++ compiles it cleanly. So it's objecting to the fact that the members are both present, causing conflicting instantiations of the template.
To get the disjunction, I think you'd want code like this:
template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};
template <class T>
struct has_members<T, std::enable_if_t<
std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> :
std::bool_constant<true> {};
This assumes you have traits to determine has_member_type1 and has_member_type2 already written.
I have a template template method which works fine when calling it with a template that is not in a namespace. However, I get a clang error when calling it with a template that is in a namespace. MSVC and gcc compile without problems, but only when I set the standard to C++17.
Here is a minimal example
#include <vector>
template<template<typename> typename Template>
Template<int> foo() {
return {};
}
template <typename T>
using my_vector = std::vector<T>;
int main()
{
foo<my_vector>(); // compiles
foo<std::vector>(); // does not compile in clang or without C++17
}
Here is a live example.
The gcc error without C++17 is:
<source>:14:5: error: no matching function for call to 'foo'
The clang error is:
<source>:14:22: error: no matching function for call to 'foo<template<class _Tp, class _Alloc> class std::vector>()'
<source>:4:15: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Template'
What changed in C++17 to allow this, and is it a bug that clang produces an error?
Vector is
template<class T, class Allocator=std::allocator<T>>
class vector;
note it takes 2 parameters.
In c++17 templates that take 2 parameters can match template<class>class if the 2nd one is defaulted; in c++14 this was not true.
As for clang in c++17, they found a bug in the standard if you implement this feature: (via #cpplearner in comments above)
(10): Despite being the resolution to a Defect Report, this feature is disabled by default in all language versions, and can be enabled explicitly with the flag -frelaxed-template-template-args in Clang 4 onwards. The change to the standard lacks a corresponding change for template partial ordering, resulting in ambiguity errors for reasonable and previously-valid code. This issue is expected to be rectified soon.
In c++14 this may work:
template<template<class...> class Z>
Z<int> foo() {
return {};
}
as there are rules letting class... match any number of arguments.
What changed in C++17 to allow this, and is it a bug that clang produces an error?
Yes the behavior changed since C++17, and Clang seems not conformance to the standard.
Note that std::vector has 2 template parameters (the 2nd one has default value), while the template template parameter Template expects only one. They don't match.
Since C++17 (CWG 150), 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<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
Consider this simple template specialization:
template<typename T, size_t I>
struct S {};
template<typename T>
struct S<T, std::tuple_size<T>::value> {};
GCC does not compile it, as it uses template parameter T in the template argument std::tuple_size<T>::value:
error: template argument 'std::tuple_size<_Tp>::value'
involves template parameter(s)
Now let's replace T with typename std::remove_reference<T>::type in tuple_size template argument:
// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};
This code still uses template parameter in template argument, but GCC compiles it without any errors or warnings. Why?
Now if we try to compile the second example using MSVS with /std:c++latest flag, it stops with error C2755:
non-type parameter of a partial specialization must be a simple
identifier
What is this strange restriction? I want to stop compile-time recursion when I becomes equal to tuple size.
So who of them is wrong: MSVS or GCC?
Note that MSVS reports the error even without any template instantiations, while GCC works fine with all of these instances:
S<std::tuple<int, float>, 9> s1;
S<std::tuple<int, float>, 2> s2;
S<int, 42> s3;
I use MSVS Community 2015 Update 3 with it's default compiler and GCC 6.2.1.
Tried Clang 3.8.0. It does not compile both snippets with an error similar to GCC's message:
error: non-type template argument depends on a template parameter of
the partial specialization
The specific section of the standard dealing with the viability of a partial class template specialization has been changed lots of times over the last few years. The original restrictionin [temp.class.spec.match] read:
A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.
Your code clearly runs afoul of that, std::tuple_size<T>::value is not an identifier.
It then changed, after cwg issue 1315, to read:
Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
But we're okay there - T is used in a non-deduced context as the first template parameter. And after template auto, it now reads:
If the template arguments of a partial specialization cannot be deduced because of the structure of its
template-parameter-list and the template-id, the program is ill-formed.
But we're okay there too. It can be deduced, you have the right "structure" - your specialization is using a non-type template parameter in the same location as the primary, and they should match fine.
I think following the resolution of 1315 (which I think is post-C++14), the code should be well-formed, but both gcc and clang reject it. An unfortunate fix would be to use two type parameters instead:
template <class T, class I>
struct S;
template<typename T>
struct S<T, typename std::tuple_size<T>::type> {};
template <size_t I>
using size_ = std::integral_constant<size_t, I>;
int main() {
S<std::tuple<int>, size_<1>> s;
}
Both gcc and clang accept that one.
I'd like to ask whether the following code sample should compile:
#include <iostream>
#include <vector>
#include <typeinfo>
using namespace std;
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
// Visual Studio requires this to be:
// using type = C<double, std::allocator<doble>>
};
int main()
{
std::cout << typeid(convert_container<std::vector>::type).name();
}
The code compiles fine with GCC 4.8.1 and Clang 3.4 but not with Visual Studio 2013. The error I get:
error C2976: 'std::vector' : too few template arguments
c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see declaration of 'std::vector'
c:\users\michał\documents\visual studio 2013\projects\transform\transform.cpp(14) : see reference to class template instantiation 'convert_container<std::vector>' being compiled
What does the standard say about this? Am I required to explicitly state all the parameters (including defaulted ones) when using the template template parameter C or is this just a bug in VC++?
Context: The issue araised from Constructor's answer to my previous question: https://stackoverflow.com/a/23874768/2617356
When searching the archives I've found this question: Default values in templates with template arguments ( C++ ) It's basically about the same problem, the question author states that default parameters for template template parameter "had to be" explicitly stated. However, the asker accepted solution that's not quite applicable in my case. The question was not about what is the standard-conforming behaviour, so I believe this is not a duplicate.
Consider the similar
template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;
This is clearly covered by the standard (14.3.3p3 if you're interested, I won't quote it, as GCC and clang do both implement the rule already), where the use of A as a template argument for B is disallowed because of the non-type template parameter. That rule makes no sense if the instantiation of a template template parameter could make use of the template template argument's default template arguments, so the behaviour of MSVC and Intel is more consistent than that of GCC and clang.
Of course, the reasoning "if this were valid, the standard would have inconsistencies" doesn't actually mean it isn't valid, only that it shouldn't be valid. To actually check what the standard says:
14.1 Template parameters [temp.param]
10 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).
8.3.6 Default arguments [dcl.fct.default]
4 Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.
Although not specifically intended to address this use of default template arguments, I think it does manage to do so. Nikos Athanasiou has already included the part of the standard that says any default template arguments of C do get used:
14.1 Template parameters [temp.param]
14 A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.
Since C's default template arguments are used, std::vector's aren't, and MSVC and Intel seem to be correct here.
And to come up with an example that clearly shows that GCC and clang cannot be considered to conform here:
template <typename = char, typename = short>
struct A { };
template <template <typename = void, typename ...> class T>
struct B {
using type = T<>;
};
Both GCC and clang treat B<A>::type as A<void, short>, taking one default template argument from T, and another from A, even though the standard disallows merging of default arguments (and hence default template arguments) in declarations in different scopes.
A workaround for you, to avoid the need to type out the allocator argument, could be to use a template alias:
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
};
template <typename T>
using vector_default_alloc = std::vector<T>;
int main()
{
std::cout << typeid(convert_container<vector_default_alloc>::type).name();
}
I cannot test on MSVC right now, but Intel accepts it, and I see no reason why this variant would be invalid.
A (seemingly related) quote from the standard 14.1 Template parameters
14 . A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.
[ Example:
template <class T = float> struct B {};
template <template <class TT = float> class T> struct A {
inline void f();
inline void g();
};
template <template <class TT> class T> void A<T>::f() { // (*)
T<> t; // error - TT has no default template argument
}
template <template <class TT = char> class T> void A<T>::g() {
T<> t; // OK - T<char>
}
— end example ]
This is the only verse posing limitations to the use of default template parameters of template template parameters (verse 9,11,12 pose limitations on the definition/specification)
As stressed in the comments, OP's case does not involve a default parameter in convert_container (so the above does not apply explicitly). IMHO there are two ways of interpreting the situation :
using type = C<double> is a type alias for a class template; that class "loses" the right to use default template parameters, since it's passed as a template template parameter and all default arguments (of that TT parameter) lie outside the scope of the "typedefing". Then VS is correct.
By tracking the instantiation process : Say a correct compiler instantiates the struct as so (it's just a type substitution - no actual representation of the actual instantiation process is implied)
struct convert_container
{
using type = vector<double>;
};
then OP's case seems fairly legit (and gcc/clang are correct)
FWIW
This compiles in VS2013
template <template <class...> class C>
using tt = C<double>;
int main()
{
std::cout << typeid(tt<std::vector>).name();
}
So the arguments of default template parameters being non legal to pass to template template parameters seems more and more shaky.