I'm porting my c++ windows code (msvc & intel) to Linux (g++). The code uses lots of templates (I like metaprogramming ;-). But I can't compile this code:
template <class TA>
struct A
{
template <class TAB> struct B;
};
template <class TC>
struct C {};
template <class TD>
struct D
{
template <class TTD> class T {};
};
template<class TA>
template<class TBA>
struct A<TA>::B : C<typename D<TA>::T<TBA> >
{
int foo;
};
g++ tells me that in definition of A::B, C class has invalid template arguments. But on msvc and intel it works well! What's the problem here?
PS: Sorry, I can't post the original code, because it's too template-complicated. But this example is virtually the same and gives the same error on g++.
Thank you.
UPDATE: I've found the problem is in TBA argument of T. g++ doensn't like usage of second template in the definition.
You need the template keyword
template<class TA>
template<class TBA>
struct A<TA>::B : C<typename D<TA>::template T<TBA> >
{
int foo;
};
GCC is correct to give a diagnostic here. This is because T cannot be looked up in the dependent scope D<TA>. The meaning of the < after it depends on whether T is a template or not. The Standard says that T shall be assumed to be not a template and thus T cannot be followed by a template argument list.
template is like typename in that it tells the compiler to treat T as a template and that the < is the start of an argument list in any case. The Standard says in paragraphs 14.2/2 and 14.2/4
For a template-name to be explicitly qualified by the template arguments, the name must be known to refer
to a template.
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
In your case, you have T appear after the nested-name-specifier D<TA> which depends on the template-parameter TA. For the typename-specifier to parse correctly, the construct D<TA>::T<TBA> must interpret T as the name of a class template, which 14.2 forbids.
On that topic, it's always a good idea to try and compile with Clang
main1.cpp:21:37: error: use 'template' keyword to treat 'T' as a dependent template name
struct A<TA>::B : C<typename D<TA>::T<TBA> >
^
template
1 error generated.
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.
First of all, apologies for the horrible title. I was experimenting with the C++20 is_detected functionality. is_detected basically takes two template parameters, one is a higher-order type which performs checking and the other one is the type to be checked. I ran into problems in the following scenario:
#include <stdio.h>
#include <type_traits>
// kind of how std::experimental::is_detected is implemented
template <template <typename> typename Checker, typename T, typename = void>
struct is_detected: std::false_type {};
template <template <typename> typename Checker, typename T>
struct is_detected<Checker, T, std::void_t<Checker<T>>>: std::true_type {};
struct Foo {
template <typename T>
using Checker = decltype(std::declval<T>().foo());
template <typename T>
static constexpr void call(T &t) {
t.foo();
}
};
template <typename T>
using GlobalChecker = decltype(std::declval<T>().foo());
template <typename T>
struct Wrapper {
template <typename U>
using LocalChecker = typename T::template Checker<U>;
// ^^^^^^^^ clang and msvc require template keyword
// gcc doesn't require it
template <typename U>
constexpr void conditional_call(U &u) const noexcept {
if constexpr (
is_detected<
typename T::Checker,// !!! COMPILE ERROR !!!
// works for
// GlobalChecker,
// LocalChecker and
// Foo::Checker, though.
std::decay_t<U>>
::value) {
Foo::call(u);
}
else {
puts("fallback");
}
}
};
int main() {
struct {
void foo() {
puts("heyy!");
}
} t;
Wrapper<Foo> w;
w.conditional_call(t); // heyy! (if foo didn't exist, then fallback)
}
If I alias the Checker in the class scope using the using LocalChecker = typename T::template Checker<U>;, it works; however, I want to learn if there is another way without using using. In addition, do I need the template in this using definition? Because Clang and GCC disagree on that.
Starting with C++17, you do not need the template keyword between a :: and a member template specialization name in certain contexts that can only name a type, including the typename-specifier syntax, which is used in the LocalChecker alias template. So clang and MSVC are wrong to reject that line without the template, and possibly haven't yet implemented this change.
C++14 [temp.names]/4:
When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent
or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
was replaced with C++17 [temp.names]/4:
The keyword template is said to appear at the top level in a qualified-id if it appears outside of a template-argument-list or decltype-specifier. In a qualified-id of a declarator-id or in a qualified-id formed by a class-head-name or enum-head-name, the keyword template shall not appear at the top level. In a qualified-id used as the name in a typename-specifier, elaborated-type-specifier, using-declaration, or class-or-decltype, an optional keyword template appearing at the top level is ignored. In these contexts, a < token is always assumed to introduce a template-argument-list. In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword template.
Since the rules above only require the keyword template when a member template specialization is named, not just the member template itself, it would seem plain T::Checker, in addition to T::template Checker, ought to work at the part of the example using is_detected. (We don't want typename, since we're naming the alias template, not a type which is a specialization of that template.) But it's not clear why adding a template should make a difference in when a compiler does or doesn't need help determining the meaning. The open CWG issue 1478 is related.
In any case, the compilers do seem to like it better with the template hint: your program with is_detected<T::template Checker,... compiles successfully on clang++, g++, and msvc: see on godbolt.
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.
I have this simple code:
template<template <class> class Generator>
class TestHelper {};
template<class Writer>
class Test
{
typedef TestHelper< Test > Helper;
};
It's works great on last g++ versions, but, in 4.4 or 4.5, I get this error:
test.cpp:7: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class> class Generator> class TestHelper'
test.cpp:7: error: expected a class template, got 'Test<Writer>'
What I'm doing wrong?
It's because inside the body of class Test<Writer>, naming Test without providing the template arguments automatically assumes the same arguments (e.g. Writer).
For example, this allows you to write the copy constructor as:
Test(const Test&);
instead of
Test::Test(const Test<Writer>&);
You can overcome this by qualifying Test with its namespace, e.g.
typedef TestHelper< ::Test > Helper;
NOTE: As Tomalek suggests, the original usage is valid in C++0x. Here is the relevant paragraph of the standard (emphasis mine), from section 14.6.1 ([temp.local]):
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-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-type-specifier 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 <>.
#Ben is probably right, but here's a totally different way to get it to compile, that doesn't use templates as args to templates.
template<class Generator> // changed this to a simpler template
class TestHelper {};
template<class Writer>
class Test
{
typedef TestHelper< typename Test :: Writer > Helper; // 2nd change
};
I made two changes. #Hugo, maybe this is what you wanted, and maybe this is what older versions of g++ did?
It's easy to get code to compile, but that doesn't mean that it does what you think it does!
What's wrong with the following sample code? It doesn't compile in GCC. Why?
template <class TA>
struct A
{
template <class TAB> struct B;
};
template <class TC>
struct C {};
template <class TD>
struct D
{
template <class TTD> class T {};
};
template<class TA>
template<class TBA>
struct A<TA>::B : C<typename D<TA>::T<TBA> >
{
int foo;
};
GCC 4.3.4 output:
error: template argument 1 is invalid
error: expected `{' before ‘>’ token
error: expected unqualified-id before ‘>’ token
Clang's error message is a bit more helpful:
error: use 'template' keyword to treat 'T' as a dependent template name
struct A<TA>::B : C<typename D<TA>::T<TBA> >
^
template
For more information consider reading the Stack Overflow C++ FAQ "Where and why do I have to put “template” and “typename” on dependent names?"
struct A<TA>::B : C<typename D<TA>::template T<TBA> >
For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template
T appears after the nested-name-specifier D<TA> which depends on the template-parameter TA. The construct D<TA>::T<TBA> must interpret T as the name of a class template and so the template keyword is mandated by the Standard at that point.