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.
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.
In short, I am trying to understand the behavior of Argument-Dependent Lookup in C++. Some statements in ISO/IEC 14882:2017 (E) regarding ADL are not clear to me. I hope somebody would clarify them to me.
According to standard,
... Furthermore, if T is a class template specialization,
its associated namespaces and classes also include: the namespaces and classes associated with the
types of the template arguments provided for template type parameters (excluding template template
parameters); the namespaces of which any template template arguments are members; and the classes
of which any member templates used as template template arguments are members.
The question is why it has to be a class template specialization? Consider the following example:
#include <iostream>
using namespace std;
namespace N
{
template <typename T>
struct A {};
template <typename T>
void func (const T&) {cout << __PRETTY_FUNCTION__ << endl;}
}
template <typename T, template <typename> class S>
class X {};
int main ()
{
typedef X<int, N::A> XX;
func(XX{});
}
As far as I can see, it compiles with both g++ and clang++, and I don't have to define something like
template <>
class X<int, N::A> {};
to make it work.
See [temp.spec]/4:
An instantiated template specialization can be either implicitly instantiated for a given argument list or be explicitly instantiated. A specialization is a class, function, or class member that is either instantiated or explicitly specialized.
X<int, N::A> is a specialization. If you were to explicitly specialize X for the arguments int and N::A, it would also be a specialization. (In C++20, the term "declared specialization" was introduced to specifically refer to the latter case; [temp.inst]/1.)
So the quoted paragraph applies to an argument of type X<int, N::A>; the namespace N is an associated namespace.
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+.
My first code fragment compiles and works fine:
template <class x> struct B1
{
template <class x> struct B2 { int getX() { return(16); } };
template <class x> struct B2<x*> { int getX() { return(20); } };
};
void main(int argc, char* argv[])
{
B1<int>::B2<int> a1;
B1<int>::B2<int*> a2;
printf("a1=%d, a2=%d.\r\n", a1.getX(), a2.getX());
}
Note that the name of template param is x in both templates and this is not confusing the compiler. The second example fails with the compiler crash (MSVC 2008), i.e. without giving any syntax error:
template <class x> struct B1
{
template <class x> struct B2 { int getX() { return(16); } };
template <class x> struct B2<x*>;
};
template <class x> template <class x>
struct B1<x>::B2<x*>
{
int getX() { return(3); }
};
My question is not on how to fix this example, this is obvious.
I looked in the standard (C++2003) on the rules of accessing params of template headers and I cannot find anything relevant. Maybe I am missing something.
While processing B1<x>, should compiler consider only params from the first template header? More likely yes. Then while processing B2<x>, should it consider both? More complex case is especially interesting when params of B1/B2 contain qualified identifiers themselves and these identifiers want to access params of the template header. For simplicity I am not giving the full example for this.
If anybody came across this and can comment or know some artickles/books, YouTube videos, etc, I would love to hear this.
UPDATE: I just tried the following with MSVC:
template <class x> struct B1
{
x qq1;
struct B2
{
int x;
};
};
This compiles and works as expected. I also tried an exe that accesses the inner data field x. This shows that MS compiler implemented hiding template params in the inner scopes. For me this is logical even if this does not comply with the standard.
Although the question has been considered answered by the comments, I'll provide some further details below. Note that my answer is based on C++11 (ISO/IEC 14882-2011) only.
Part 1: Can a template parameter be re-used as template-parameter of a nested member template?
There are two key statements in the standard about the status of template parameters (specifically, type-parameters – which are the only kind relevant to the question). The first statement describes them as having the same status as typedef-names:
(§14.1/3) A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared with class or typename) or template-name (if declared with template) in the scope of the template declaration. [...]
And regarding the possible redeclaration of typedef-names, we have:
(§7.1.3/6) In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [...]
(Remark: The rule above seems to apply to the use of typedef specifiers only, although alias declarations (§7.1.3/2) and template parameter declarations (§14.1/3) can be used to declare typedef-names as well. That is, the rule above does not explicitly rule out the use of an alias declaration or indeed a template parameter to redeclare a typedef-name within the same scope, although that is clearly the intended meaning. The wording should be "no typedef-name declaration shall be used" instead of "a typedef specifier shall not be used".)
What it means is that you cannot do this:
{
typedef int T;
typedef float T;
}
because the second declaration occurs in the same scope in which T was originally declared. However, this:
{
typedef int T;
{
typedef float T;
}
}
is perfectly legally according to the rule above, because the second declaration is in a block scope which (although the first T is still valid there) is not the scope in which T was originally declared.
Due to §14.1/3 quoted above, we must assume that the rule applies to template parameter declarations as well, hence something like
template <typename X> template <typename X>
struct Outer<X>::Inner<X> {
};
is illegal even on the simple basis that it implies the declaration of the same typedef-name twice within the same scope.
However, in a case like
template <typename X>
struct Outer {
template <typename X>
struct Inner {
};
};
one may argue that the second declaration of template <typename X> applies to a nested scope. Fortunately, the standard does provide the following second statement about the status of template parameters:
(§14.6.1/6) A template-parameter shall not be redeclared within its scope (including nested scopes). A template-parameter shall not have the same name as the template name.
[Example:
template<class T, int i> class Y {
int T; // error: template-parameter redeclared
void f() {
char T; // error: template-parameter redeclared
}
};
template<class X> class X; // error: template-parameter redeclared
— end example ]
As clearly stated, the no-redeclaration rule that applies to any typedef-name within the declaration scope, applies to nested scopes as well in the case of template parameters.
Here is an example to motivate why I think that rule is actually useful, too. Consider:
template <typename T1>
struct Outer
{
static const int outerID = 5;
template <typename T2>
struct Inner
{
int id1() { return Outer<T1>::outerID; }
int id2() { return Outer::outerID; }
int id3() { return outerID; }
};
};
The three functions of the inner template all refer to the same static member of the outer class, but in three different ways. id2() and id3() do this because §14.6.2.1/4 requires Outer::outerID and outerID to be interpreted as referring to the current instantiation, which is Outer<T1>::outerID.
If we now replace the template parameter of the inner template with T1, the same as for the outer template, the meaning of id1() would change (because Outer<T1> would now refer to whatever the definition of T1 in the inner template is), but id2() and id3() would – most naturally – still refer to the current instantiation of the template outerID belongs to. Therefore, id1() may return a different value than id2() and id3(), which would be most awkward.
Part 2: In a partial specialization of a member template, can template parameters of the member be used as template arguments of the enclosing class, and vice versa?
Another problem addressed by the question is whether in a specialization or outside-of-class definition of a member template, such as
template <typename A> template <typename B>
struct Outer<A>::Inner<B> {
// ...
};
the template argument list of the outer template (i.e. <A> in this case) could make use of parameters defined for the inner template (i.e. B in this case), and vice versa.
Let's first consider the special case given in the question, where the two parameters are identical:
template <typename A> template <typename A>
struct Outer<A>::Inner<A> {
// ...
};
Although we have ruled this out in Part 1 already due to the re-declaration problem, we can still consider the intended meaning of this syntax, which is: Define an explicit specialization of the inner class template, in which the template argument is assumed to be same as for the outer template. The syntactically correct way to write that is
template <typename A> template <>
struct Outer<A>::Inner<A> {
// ...
};
i.e. the second parameter list would be empty. Unfortunately, this would amount to an explicit specialization of a member template, which is illegal unless the enclosing template is also explicitly specialised (§14.7.3/16).
However, if we consider a partial, rather than an explicit specialization of a member template with two or more parameters, the declaration becomes legal:
// Primary declaration
template <typename A>
struct Outer {
template <typename A, typename B>
struct Inner {
// ...
};
};
// Partial specialization of member template
template <typename A> template <typename B>
struct Outer<A>::Inner<B,A> {
// ...
};
We have now used the template argument A of the enclosing template as specialised second argument for the inner template as well. Any instantiation of the template that uses the same data type for the template argument of the outer class as well as the second template argument of the inner class, e.g.
Outer<int>::Inner<float,int> myvar;
will instantiate the specialization defined above.
Hence, using template parameters of an enclosing class in the template argument list of a member template is without problems, and the reason is that by the time Inner<B,A> is evaluated, A already has the status of a typedef-name defined at the scope level of Outer.
But doing it reversely, e.g.
template <typename A> template <typename B>
struct Outer<B>::Inner<B,A> {
// ...
};
will not work, because B is a typedef-name of the Inner scope only. The Standard states:
(§14.5.2/1) [...] A member template of a class template that is defined outside of its class template definition shall be specified with the template-parameters of the class template followed by the template-parameters of the member template.
[Example:
template<class T> struct string {
template<class T2> int compare(const T2&);
template<class T2> string(const string<T2>& s) { /∗ ... ∗/ }
};
template<class T> template<class T2> int string<T>::compare(const T2& s) {
}
— end example ]
I interpret this to mean that the two template parameter lists (one followed by the other) are kept separately, not considered as one combined list of template parameters. Hence in
template <typename A> template <typename B>
struct Outer<B>::Inner<B,A>
the interpretation of Outer<B> cannot make use of the second template parameter list, and B will be undefined, whereas in the previous case
template <typename A> template <typename B>
struct Outer<A>::Inner<B,A>
the interpretation of Inner<B,A> is possible because A has the status of a typedef-name declared in a scope that Inner is part of, i.e. it will be successfully interpreted as Inner<B,Outer::A>, which in turn is the same as Inner<B,Outer<A>::A>.
C++ Standard
Section 14/2 :
In a function template declaration,
the declarator-id shall be a
template-name (i.e., not a
template-id). [Note: in a class
template declaration, if the
declarator-id is a template-id, the
declaration declares a class
template partial specialization.
What is the difference between a template-name, template-id and a type-id?
Does the above quote mean we cannot write something like
template <>
void templatefunction<int>(){ // ...}
or have I misunderstood the point?
The template-name is the name of the template. In your example, templatefunction is a template-name.
The template-id is the name of the template with the template arguments list. In your example, templatefunction<int> is the template-id. A template-id names a template specialization.
A type-id names a type. A template-id is a type-id; a template-name is not (because it does not name a type; it names a template).
The text you cite from 14/2 concerns a template-declaration, which declares a primary template. Your example is not a template-declaration, it is an explicit-specialization (14.7.3/1).
A declarator-id is the syntactical element that specifies the name in a simple-declaration ("type name;"). In the following "A" and "B::C" is the declarator-id
int A;
int B::C;
int A();
int *A;
int A[42];
template<typename T> void A();
A type-id syntactically is roughly a simple-declaration where the declarator-id is missing. A type-id is used as the syntactical element in a template type argument and in a cast.
int // type-id
int* // type-id
int[] // type-id
int() // type-id
int(*)() // type-id
A template-name is the name of a template. Syntactically it appears before a template-argument list. The above quote misuses "template-name" and "declarator-id", because a template-name is a plain identifier and does not contain any qualifiers. C++0x has changed the text to
In a function template declaration, the last component of the declarator-id shall be a template-name or operator-function-id (i.e., not a template-id).
(The last part appears in cases such as operator+()). Even the C++0x text misses some cases - see this defect report.
The misuse of "declarator-id" happens in the note. The note was replaced by C++0x with
[ Note: in a class template declaration, if the class name is a ... — end note ]
In class template declarations, the name specified syntactically is a class-name instead of a declarator-id. The relation of class-name and declarator-id is as follows (very simplified...)
class class-name { ... } declarator-id;
class foo { ... } bar;
In class template declarations, there may not be a declarator-id specified.
A template-id is a template-name followed by a template-argument list.
The quote means that in a function template declaration, the name must not be a template-id. In your example you declare a function instead of a template. There are still cases where an explicit specialization declares a template, though. But that can only happen for member function templates
template<typename T>
struct A {
template<typename U>
void f();
};
// this explicit specialization *contains* a template declaration and
// declares an identifier (a template-name) qualified by A<int>::
template<> template<typename U>
void A<int>::f() { }
From C++ Templates: The Complete Guide By David Vandevoorde, Nicolai M. Josuttis
8.3
Explicit template arguments: A template name can be followed by explicit template argument values enclosed in angle brackets. The resulting name is called a template-id.
For example:
template <typename T>
struct Demo{
// ...
};
int main()
{
Demo <int> d; // Demo is the template name, Demo<int> is the template-id
// ...
}
In a function template declaration, the declarator-id shall be a template-name (i.e., not a template-id).
For example (from what I have understood):
class A {
public:
template <typename T> void f(T);
template <typename T> struct X { };
};
class B : public A {
public:
using A::f; // fine
using A::X // fine
};
class C : public A {
public:
using A::f<int>; // ill formed, declarator-id shall not be a template id
using A::X<double> // ill formed, declarator-id shall not be a template id
};
Someone please correct me if I am wrong.