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.
Related
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.
(This question is not about template template arguments.)
I just discovered that GCC compiles such code
template <typename A, typename B>
struct P {};
template <typename A>
template <typename B>
using Q = P<A, B>;
where Q is a doubly-templated name.
But I can't use this. When I write Q<short><long>, I get
template_template.cpp:10:5: error: ‘Q<short int>’ is not a template
Q<short><long>{};
^~~~~~~~
template_template.cpp:10:20: error: invalid use of incomplete type ‘Q<short int>’
Q<short><long>{};
^
template_template.cpp:2:8: note: declaration of ‘Q<short int>’
struct P {};
Why is the first snippet compiled?
Is there a syntax to convince the compiler that Q<short> is actually a template?
// GCC 6.3.0
The C++14 standard says in 14p1:
The declaration in a template-declaration shall
— declare or define a function, a class, or a variable, or
— define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
— define a member template of a class or class template, or
— be an alias-declaration
Here the declaration within the template-declaration is none of the above (it is another template-declaration, which itself contains an alias-declaration), and therefore the code is invalid.
The relevant parts of the grammar are:
template-declaration:
template <template-parameter-list> declaration
alias-declaration:
using identifier attribute-specifier-seqopt = type-id ;
where a declaration can be a template-declaration, an alias-declaration, or other types of declarations.
Note that the grammar itself accepts the given code, but the additional restrictions in the text above make it invalid.
Consider an example:
#include <type_traits>
#include <string>
template <template <class> class TT> //#1
struct Foo {
static void foo() {
static_assert(std::is_same_v<decltype(TT("abc")), TT<std::string>>);
}
};
template <class T>
struct Bar {
Bar(T) {}
};
template <class T>
Bar(T) -> Bar<std::string>; //#2
int main() {
Foo<Bar>::foo();
}
[clang] as well as [gcc] both seem to use user provided deduction guides (#2) when deducing the template parameter of template template parameter (#1). Is it a standard compliant feature?
Yes, this is standard compliant.
According to [dcl.type.simple]/2:
A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type ([dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name.
And [temp.param]/3:
A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared without template) or template-name (if declared with template) in the scope of the template declaration.
TT is a type-parameter declared with template, which makes it a template-name and hence a placeholder for a deduced class type. All the usual rules apply just fine.
I'm trying to do something like the following:
template <typename T>
struct A
{
template <typename U>
struct AA
{
};
};
template <typename V, template <typename> class W = A<V>::AA> // Causes C3202
struct B
{
};
But the Visual Studio 2010 compiler spits out:
error C3202: 'AA' : invalid default argument for template parameter '', expected a class template
If I replace B with the following template:
// Replace "T" with "int"
template <typename V, template <typename> class W = A<int>::AA>
struct B
{
};
the code compiles fine, but isn't what I want. If the original isn't legal C++, is there an alternative that provides a similar interface for users of the "B" template?
Your code is not a valid C++ code. See the quotes below.
C++03
14.2 Names of template specializations [temp.names]
14.2/4
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.
14.2/5
If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates.] Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter.]
C++11
14.2 Names of template specializations [temp.names]
14.2/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.
14.2/5
A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template. [ Note: The keyword template may not be applied to non-template members of class templates. — end note ] [ Note: As is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or . is not dependent on a template-parameter, or the use does not appear in the scope of a template. — end note ] [ Example:
// ...
template <class T> struct B {
template <class T2> struct C { };
};
// OK: T::template C names a class template:
template <class T, template <class X> class TT = T::template C> struct D { };
D<B<int> > db;
— end example ]
Standard-compliant code
So the correct syntax in this situation is:
template <typename T>
struct A
{
template <typename U>
struct AA
{
};
};
template <typename V, template <typename> class W = A<V>::template AA>
// ^^^^^^^^^^^
struct B
{
};
Unfortunately VC2010 doesn't understand the valid syntax, too.
Depending on your definition of "similar", you could change your B template definition into
template <typename V, typename W = A<V>>
struct B
{
};
and inside struct B, you can access the inner template as
W::template AA< some_type >
Your users would, however, need to provide the AA-like template wrapped in a class, just as your struct A.
You could also lower your requirements on compiler compatibility and require MSVC 2013+.
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.