The following code compiles using both Clang and GCC, even though Foo_t<T>::Bar doesn't have typename in front of it:
struct Foo {
using Bar = int;
};
template<class...>
using Foo_t = Foo;
template<class T>
void f(){
Foo_t<T>::Bar b; // No typename!
}
int main(){
f<int>();
}
Should it compile?
Introduction
Foo_t<T>::Bar might look like a dependent-name, but it isn't since the template-arguments passed to the alias-declaration are not used when determining what the qualified-id Bar is referring to.
The code is well-formed.
What does the Standard (N3337) say?
14.5.7/2 Alias templates [temp.alias]
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.
A.6 Declarations [gram.dcl]
alias-declaration:
using identifier attribute-specifier-seq_opt = type-id ;
What is the Standard really saying?
Since there are no template-parameters in the type-id of Foo_t, the template alias-declaration is always directly equivalent to Foo, no matter what template-arguments we pass to it.
template<class... Ts>
using Foo_t = Foo;
// ^--- "Foo" = type-id
Replacing the usage of Foo_t<T> with the equivalence of the template alias-declaration leaves us with the following:
template<class T>
void f(){
Foo::Bar b; // ok, nothing here depends on `T`
}
With some more digging, this is CWG issue 1390.
The issue description is
According to 14.6.2.1 [temp.dep.type] paragraph 8, a type is dependent
(among other things) if it is
a simple-template-id in which either the template name is a template
parameter or any of the template arguments is a dependent type or an
expression that is type-dependent or value-dependent
This applies to alias template specializations, even if the resulting
type does not depend on the template argument:
struct B { typedef int type; };
template<typename> using foo = B;
template<typename T> void f() {
foo<T>::type * x; //error: typename required
}
Is a change to the rules for cases like this warranted?
And there's a note in that issue:
Notes from the October, 2012 meeting:
CWG agreed that no typename should be required in this case. In some
ways, an alias template specialization is like the current
instantiation and can be known at template definition time.
The issue is still in "drafting" status, but it looks like the compiler vendors are already implementing the intended resolution.
Related
Consider the following c++ program:
class A
{
protected:
int x;
};
template<typename X>
using B = A;
template<typename T>
class C : public B<T>
{
public:
void f()
{
x = 0;
}
};
int main()
{
}
When compiled with clang and gcc using -std=c++17 -pedantic-errors as compilation options they behave differently: Clang compiles without any errors, but gcc gives a compilation error about not being able to lookup the identifier x.
What does the c++ standard say in this case? Are both behaviours allowed, or does one of the compilers have a bug in this case?
Compiler explorer link: https://godbolt.org/z/EYvYrr
This looks similar to CWG1390, and looks like Clang's behavior is consistent with CWG agreement on how such alias templates should be handled (substituted eagerly, at template definition time):
1390. Dependency of alias template specializations
According to 17.7.2.1 [temp.dep.type] paragraph 8, a type is dependent (among other things) if it is
a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent
This applies to alias template specializations, even if the resulting type does not depend on the template argument:
struct B { typedef int type; };
template<typename> using foo = B;
template<typename T> void f() {
foo<T>::type * x; //error: typename required
}
Is a change to the rules for cases like this warranted?
Notes from the October, 2012 meeting:
CWG agreed that no typename should be required in this case. In some ways, an alias template specialization is like the current instantiation and can be known at template definition time.
For C++17, GCC is correct here.
9 A type is dependent if it is
a template parameter,
[...]
a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent
type or an expression that is type-dependent or value-dependent or is
a pack expansion [ Note: This includes an injected-class-name of a
class template used without a template-argument-list. — end note ] ,
or
B<T> is a simple-template-id, its argument is a dependent type, therefore the type denoted by B<T> is dependent. When using a dependent type as a base class, any inherited member must be either fully qualified by it's base class name, or accessed via this->.
Consider the following code:
#include <type_traits>
template<template<class...> class T, class... U>
struct is_specialization_of : std::false_type{};
template<template<class...> class T, class... U>
struct is_specialization_of<T, T<U...>> : std::true_type{};
template<class T, class U = int>
struct test{};
// (1) ok
static_assert(is_specialization_of<test, test<int>>::value, "1");
template<class T>
using alias = test<T>;
// (2) fails
static_assert(is_specialization_of<alias, alias<int>>::value, "2");
int main()
{
}
Why does (2), i.e. static_assert that uses alias template, fail?
How does the template argument deduction process in (2) differ from the one in (1)?
This is CWG issue 1286. The question is: are alias and test equivalent? There used to be an example in [temp.type] which suggested that y and z have the same type here:
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
The example was corrected as part of CWG defect 1244 - which indicated correctly that there is no wording in [temp.alias] that actually specifies that alias templates are equivalent to the templates they alias. The only wording there refers to equivalence of alias template specializations:
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.
The intent is apparently that y and z do have the same type in this example, meaning that Z and Y are actually equivalent. But unless and until the wording in the resolution is adopted, they are not. Today, alias and test are not equivalent but alias<int> and test<int> are. This means that is_specialization_of<alias, alias<int>> is is_specialization_of<alias, test<int>>, where alias is unique from test, which would not match your partial specialization and thus be false_type.
Moreover, even with the adoption of the wording in #1286, test and alias are still not equivalent for the obvious reason that test takes two template parameters and alias takes one template parameter. The example in the resolution wording mimics your example and clarifies the intent here:
template<typename T, U = T> struct A;
// ...
template<typename V>
using D = A<V>; // not equivalent to A:
// different number of parameters
I think that name of alias template without template arguments list is not equivalent to the name of associated type. Because standard specifies only one such situation:
14.5.7 Alias templates [temp.alias]
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. [Note: An alias template name is never deduced.—end note ]
and this works fine:
static_assert(is_specialization_of<test, alias<int>>::value, "2");
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.
Consider the following code:
#include <type_traits>
template<template<class...> class T, class... U>
struct is_specialization_of : std::false_type{};
template<template<class...> class T, class... U>
struct is_specialization_of<T, T<U...>> : std::true_type{};
template<class T, class U = int>
struct test{};
// (1) ok
static_assert(is_specialization_of<test, test<int>>::value, "1");
template<class T>
using alias = test<T>;
// (2) fails
static_assert(is_specialization_of<alias, alias<int>>::value, "2");
int main()
{
}
Why does (2), i.e. static_assert that uses alias template, fail?
How does the template argument deduction process in (2) differ from the one in (1)?
This is CWG issue 1286. The question is: are alias and test equivalent? There used to be an example in [temp.type] which suggested that y and z have the same type here:
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
The example was corrected as part of CWG defect 1244 - which indicated correctly that there is no wording in [temp.alias] that actually specifies that alias templates are equivalent to the templates they alias. The only wording there refers to equivalence of alias template specializations:
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.
The intent is apparently that y and z do have the same type in this example, meaning that Z and Y are actually equivalent. But unless and until the wording in the resolution is adopted, they are not. Today, alias and test are not equivalent but alias<int> and test<int> are. This means that is_specialization_of<alias, alias<int>> is is_specialization_of<alias, test<int>>, where alias is unique from test, which would not match your partial specialization and thus be false_type.
Moreover, even with the adoption of the wording in #1286, test and alias are still not equivalent for the obvious reason that test takes two template parameters and alias takes one template parameter. The example in the resolution wording mimics your example and clarifies the intent here:
template<typename T, U = T> struct A;
// ...
template<typename V>
using D = A<V>; // not equivalent to A:
// different number of parameters
I think that name of alias template without template arguments list is not equivalent to the name of associated type. Because standard specifies only one such situation:
14.5.7 Alias templates [temp.alias]
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. [Note: An alias template name is never deduced.—end note ]
and this works fine:
static_assert(is_specialization_of<test, alias<int>>::value, "2");
Sorry for the funny title.
Prior to C++0x, there are restrictions in the use of function-local structs (“local types”) as template arguments. My question is essentially if similar restrictions apply to anonymous structs. Specifically, in the context of a trait class:
template <typename T>
struct trait;
template <>
struct trait<int> {
typedef int type;
};
template <typename T>
struct trait<std::basic_string<T> > {
typedef struct {
T value;
} type;
};
trait<std::string>::type foo; // Is this valid?
template <typename T>
void f() { }
f<trait<std::string>::type> >(); // Is this?
template <typename T>
void g() { f<typename trait<T>::type>(); }
g<std::string>(); // And this?
Is this valid and reliable? It compiles in recent versions of GCC and LLVM but I’m still insecure whether this is strictly valid, and whether it’s understood by VC++ and ICC.
For reference, the quote from the linked question in 14.3.1/2:
A local type, a type with no linkage,
an unnamed type or a type compounded
from any of these types shall not be
used as a template argument for a
template type parameter.
My interpretation is that the typedef struct is creating an alias to an unnamed type and that it thus can't be used as a template type parameter. Further note that additionally in C typedef struct {} Foo; is treated rather differently from struct Foo {}; giving precedent that the two forms are not equivalent (although admittedly that difference doesn't appear in C++).
Thus it would appear your first example works (since it's not using the unnamed type as a template type parameter), while the second and third examples would be technically invalid (since they do use it as a template type parameter).
Finally in closing I have to ask, is there a reason you can't name the struct instead of typedefing it?
EDIT: From 7.1.3/1:
...A typedef-name is thus a synonym for
another type. A typedef-name does not
introduce a new type the way a class
declaration (9.1) or enum declaration
does...
This strongly implies that using typedef in such a way does not introduce a type suitable for use as a template type-parameter.
In the upcoming standard that restriction is removed from the language. The standard says in
14.3.1 [temp.arg.type] /1
A template-argument for a template-parameter which is a type shall be a type-id.
And a typedef is a valid type-id. As a matter of fact the next paragraph contains such an example:
14.3.1 [temp.arg.type] /2
template <class T> class X { };
template <class T> void f(T t) { }
void f() {
typedef struct { } B;
B b;
X<B> x3;
f(b);
}
(Where I have trimmed most of the other examples) The example shows that an unnamed type can be used as a class template argument both in class templates and function templates.
A typedef declaration that defines an anonymous class and a typedef-name for that class, the typedef-name is the name of the class for linkage purposes. It is therefore legal to use that class as a template parameter if it meets the other criteria.
See 7.1.3p5 of the C++03 standard
If the typedef declaration defines an
unnamed class (or enum), the first
typedef-name declared by the decla-ration to be that class type (or enum
type) is used to denote the class type
(or enum type) for linkage purposes
only (3.5). [Example:
typedef struct { } *ps, S; // S is the class name for linkage purposes
This is 7.1.3p9 in the C++0x FDIS.
FWIW, this code compiles OK with MSVC2010 (modulo typos).
Well, that is equivalent to
template <typename T>
struct trait<std::basic_string<T> > {
struct type {
T value;
};
};
which is completely legitimate.