Why double colon in template argument works? - c++

While answering one of SO questions I came across code like this:
template<class T, class... Ts> void foo();
template <class T, class T::value_type>
void foo() { }
It was presented as code specializing foo template function, which is not right, but that is not my issue here. I would like to know why does compiler allow construcion like this: class T::value_type in the template parameter. I mean, it is pretty clearly wrong, I cannot come up with any situation that scope operator may be part of the argument name (either template or function). And so I have two qestions:
Does standard allow it or is it an overlook in compilers?
If standard allows it, why is it so? Are there any use cases?

As mentioned in the comments, it's an elaborated type specifier. Best explained with an example:
int main() {
struct foo {}; // ok
int foo = 0; // ok
int test = foo; // ok, refers to variable 'foo'
foo a; // error, 'foo' refers to variable
struct foo b; // ok, 'struct' means that name lookup searches for classes only
}
In essence, you can think of them (struct/class, enum) as a more restricted typename, as they only allow classes or enums respectively. Also do note that typename is allowed in your original example!
template<class T, class... Ts> void foo();
template <class T, typename T::value_type> // Ok, value_type needs to be a type
// ^^^^^^^^^^^^^^^^^^^^^^^ it's a non-type template parameter
void foo() { }
It is needed when you have a type and a variable with the same name, or to specify what the thing is when you have a dependent name (i.e. in class T::value_type, value_type is a class, without the class before, it would have been a value. Normally, a typename is used.)
I cannot come up with any situation that scope operator may be part of the argument name
Your only thinking of type template parameters here; non-type template parameters can very well use a scope operator to name the type.

Related

C++ template class = typename

what template <class = typename T::type> means? Can you refer me to some blog, specification describing this?
The question originaly came from explanation of sfinae on cpp reference
template <typename A>
struct B { typedef typename A::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
First, I'll explain typename T::type. This is simply the access of a member type. Here's an example of accessing a member type:
struct foo {
using bar = int;
};
int main() {
foo::bar var{};
// var has type int
}
So why the typename? It simply means we want to access a type. Since we are in a template and T is an unknown type, and foo::bar could also mean accessing a static variable. To disambiguate, we denote that we effectively want to access a type by explicitly typing typename.
Okay, now what does the class = means?
The class = means the same thing as typename =. When declaring template type parameters, we introduce then using class or typename:
template<typename A, typename B>
struct baz {};
But as any parameters in C++, the name is optional. I could have wrote this and the following is completely equivalent:
template<typename, typename>
struct baz {};
Also, you know in function parameters, we can specify default values? Like that:
void func(int a, int b = 42);
int main () {
func(10); // parameter b value is 42
// We are using default value
}
We can also omit parameter names:
void func(int, int = 42);
Just like function parameters, template parameters can have it's name omitted, and can have a default value.
template<typename, typename = float>
struct baz {};
baz<int> b; // second parameter is float
Putting it all together
Now we have this declaration:
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
Here we declare a function that takes an int as parameter, and has three template parameter.
The fist parameter is a simple named parameter. The name is T and it's a type template parameter. The second is also a type parameters, but it has no name. However, it has a default value of T::type, which is a member type of T. We are explicitly telling the compiler that T::type must be a member type of T by specifying typename. The third parameter is similar to the second.
This is where SFINAE kicks in: when a default parameter is used, but T::type as as a member type don't exist, how can you assign the second template parameter to that? We can't. If T::type don't exists, we cannot assign the second template parameter. But instead of making it an error, the compiler will simply try another function, because there is a chance another function would be callable.
This is quite similar to simple overloading. You have the f function. It takes a float parameter, an another overload that takes a std::string. Imagine you call f(9.4f). Does the compiler choke because a std::string is not constructible from a float? No! The compiler ain't stupid. It will try another overload, and will find the float version and call it. In SFINAE a similar analogy can be made. The compiler won't stop because there's some overload somewhere that needs an undefined type in a template parameter. It will try another overload.

All versions of GCC struggle with a template that has the default type in a definition

I wasted countless hours to pinpoint an issue with gcc. I wanted to test our code base with another compiler to look for more warnings that Clang might have missed. I was shocked that practically half of the project stopped to compile due to failure of template argument deduction. Here I've tried to dumb my case down to the simplest piece of code.
#include <type_traits>
struct Foo
{ };
// This is a template function declaration, where second template argument declared without a default
template <typename T, typename>
void foo(const Foo & foo, T t);
// This is a template function definition; second template argument now has a default declared
template <typename T, typename = typename std::enable_if<1>::type>
void foo(const Foo & foo, T t)
{
}
int main(int argc, char ** argv)
{
foo(Foo{}, 1);
return 0;
}
Ignore a 1 in the std::enable_if<1>. Obviously it's a constant value just to not complicate things when it does not matter.
This piece of code compiles[1] with clang (3.4 through 4.0), icc (16, 17), Visual C++ (19.00.23506). Basically, I couldn't find any other c++11 compiler that, except gcc (4.8 through 7.1), does not compile this piece of code.
The question is, who's right and who's wrong here? Does gcc behave according to the standard?
Obviously this is not a critical issue. I can easily move std::enable_if to the declaration. The only victim would be aesthetics. But it is nice to be able to hide an ugly 100 characters long std::enable_if piece of code, that is not immediately relevant for the user of the library function, in the implementation.
Live example on godbolt.org.
What the standard says ([1] page 350):
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). [
Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
So GCC is wrong here. It ignores default template arguments in declarations.
Not all declarations, only function template declarations. Class template declarations are okay:
#include <type_traits>
template <typename T, typename>
struct Foo;
template <typename T, typename = typename std::enable_if<1>::type>
struct Foo
{
T t;
};
int main()
{
Foo<int> foo;
return 0;
}
Live example on godbolt.org
Probably it is due to the nature of how non-default arguments are deduced. In the function template they are deducted from function arguments. In the class template we have to specify them explicitly.
Anyway, I have created a bug report.

Is it possible to extract type from a pointer template parameter?

Is it possible to define a template which takes a single pointer parameter and extracts the type pointed to?
extern int three = 3;
typename examine<&three>::pointed_type // int
There's already std::remove_pointer<T>::type in <type_traits> (C++11) that you can use.
Yes, you would use partial specialization to achieve this, similar to the following:
template<typename T>
struct examine {
typedef T pointed_type;
};
template<typename T>
struct examine<T*> {
typedef T pointed_type;
};
To refer to your comment about usage of non-type template parameters, consider the following:
template<typename T, T* p>
struct foo { };
extern int three = 3;
foo<decltype(three), &three> bar;
As you can see, foo can indeed take a template parameter of &x, but to do so we first need to give it a type parameter (or restrict it to int*, which does not help at all). If this is not done, the name T is not that of any type when the template parameter p is defined.
There is no way at all to perform any kind of automatic deduction of T, since that would require the template arguments to be defined in the other order.
The closest you can get without using decltype is by defining a function template that gets a non-template argument that will cause the template type parameter to be deduced like so
template<typename T>
void deduce_argument_type(T const&)
{
// here, the type T is (close to) that of your argument
}
deduce_argument_type(&three); // uses deduce_argument_type<int*>
Again, you cannot use this to get around the restriction on defining T before passing &three, since template parameter deduction only deduces any types to the right of those template parameters you passed.

How do I use the class-name inside the class itself as a template argument?

I've got a templated class having two template paramters
template <class T, class U> class A /* ... */
and another template class that accepts a template class with two arguments as template parameter.
template <class T, class U, template<class X, class Y> class Z>
class B
{
typedef typename Z<T,U>::pointer pointer;
};
Is it impossible to create an instance of B in A where Z is A?
template <class T, class U>
class A
{
public:
B<T,U,A> foo (void) // compiler complaining here
{
B<T,U,A> test; // and here
return test;
}
};
A free function doing the same thing isn't problematic at all.
template<class T, class U>
B<T, U, A> bar (void)
{
B<T,U,A> test;
return test;
}
In other words: Is there any rule I didn't fell over yet that prevents me from using the name of the class I am in as a template argument?
The code is:
template <class T, class U, template<class X, class Y> class Z>
class B
{
typedef typename Z<T,U>::pointer pointer;
};
template <class T, class U>
class A
{
public:
B<T,U, A> foo (void)
{
B<T,U,A> test;
return test;
}
};
template<class T, class U>
B<T, U, A> bar (void)
{
B<T,U,A> test;
return test;
}
int main (void)
{
return 0;
}
And the MSVC 2012 compiler gives a Compiler Error 3200.
'A<T,U>' : invalid template argument for template parameter 'Z', expected a class template
Your compiler, MSVC, seems to follow the general rule laid down in §14.6.2.1/1 C++11:
A name refers to the current instantiation if it is, [...] in the definition of a class template, [...] the injected-class name [...] of the class template [...]
Inside the definition of class template A, the name A can be used because it is "injected" into the local (class) scope of A. Therefore, and famously, you can use A as well as A::A, as well as A::A::A and so on, to refer to A. By the rule quoted above, all of these expressions refer to the current instantiation (i.e. a specific type like A<int,float>), and not to the name of template A itself.
However, there is another rule, in §14.6.1/1:
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 [...] as a template-argument for a template template-parameter [...] 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 <>.
(Note that in C++03, there is no such exception for template template parameters; 14.6.1/1, in fact, is worded entirely differently. Under C++03, MSVC's interpretation of the rules was probably correct.)
Given the C++11 rule, however, in the member declaration
B<T,U,A> test;
inside the definition of A, the name A is clearly used as an argument for a template template parameter and therefore must be interpreted as template name, not as type name referring to the current instantiation.
However, it is not uncommon that compilers are confused in situations like this. There are two valid ways to tell them how to interpret A:
Using the so-called normal name ::A rather than the injected one:
B<T,U,::A> test;
This is possible because of §14.6.1/5 (which was 14.6.1/2c in C++03):
When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used, it always refers to the class template itself and not a specialization of the template. [...]
Using the injected one explicitly, but designating it as a template:
B<T,U,A::template A> test;
Both methods have been confirmed as solving this problem in MSVC.
If class A is defined before class B, I'm getting the error: 'B' does not name a type (in other words, B is not defined yet). Is this the error you are getting? It can be fixed either by placing B in front of A, or, if the two are referencing each other, by forward-declaring class B before A like this:
template <typename T, class U, template<typename X, class Y> class Z> class B;

Accessing template parameters of the member templates

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>.