I am in need of your help once again...
I had the following code, which was causing explicit specialization in non-namespace scope error:
namespace __test
{
template <int A, int B, typename C> class Test
{
template <int V> void check(C & a) { }
template <> void check<0>(C & a) { } //error: explicit specialization in non-namespace scope 'class __test::Test<A, B, C>'
};
}
Since I already know how to fix this kind of errors, I defined specialization outside of the class scope, however I got another error - ... used without template parameters:
namespace __test
{
template <> void Test::check<0>(C & a) { } //error: 'template<int A, int B, class C> class __test::Test' used without template parameters
}
I'm probably just being stupid, but I don't understand the cause of this problem and I don't know how to fix it... Please help!
By my reading of the standard, what you want to do appears to be legal. Quoting §14.7.3/18:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition.
As you're explicitly specializing a member function template rather than a class member template, it should be fine; however, neither Comeau, GCC, nor VC++ allow the following, which should be correct syntax:
namespace test
{
template<int A, int B, typename C>
class Test
{
template<int V>
void check(C& a) { }
};
template<int A, int B, typename C>
template<>
void Test<A, B, C>::check<0>(C& a) { }
}
Comeau says error: a template declaration containing a template parameter list may not be followed by an explicit specialization declaration, which makes sense if we apply the rule in §14.7.3/18 to member function templates as well
GCC says invalid explicit specialization before '>' token; enclosing class templates are not explicitly specialized, which again makes sense if we apply the rule in §14.7.3/18 to member function templates as well
VC++ says error C2768: 'test::Test<A,B,C>::check' : illegal use of explicit template arguments, which isn't a helpful error message, but generally falls in line with the others
My guess is that there must be a defect report filed that also disallows explicit specializations of member function templates when the enclosing class templates are not explicitly specialized as well; however, I can't say this definitively since the wording for §14.7.3/18 hasn't changed between the C++03 standard and the C++0x FDIS (which it would if a DR was filed against C++03 and accepted).
You need to either fully specialize everything, like this:
namespace __test {
template <int A, int B, typename C>
class Test
{
template <int V> void check(C & a) { }
};
template <>
template <>
void Test<1, 2, int>::check<0> (int &)
{
}
}
Or use a helper structure to avoid trying to partially specialize template method of a template class (which GCC and many others won't understand):
namespace __test {
template <typename C, int V>
struct TestHelper
{
static void check (C & a)
{
}
};
template <typename C>
struct TestHelper<C, 0>
{
static void check (C & a)
{
}
};
template <int A, int B, typename C>
class Test
{
template <int V> void check(C & a)
{
TestHelper<C, V>::check (a);
}
};
}
Related
I must be missing something obvious here because this really came to me as a surprise.
The following code gives me the error: error: missing template arguments before ‘a’
template<int n=0>
class A {};
...
A a;
...
Is there a reason why a template with 1 parameter declared with a default value has to be instantiated specifying a value for it?
Can someone quote the standard?
A is still a template class. But you can omit the template parameter though:
template<int n=0>
class A {};
int main() {
A<> a;
// ^^
return 0;
}
See the live demo.
I think1 the relevant standard section is here
14.7 Template instantiation and specialization
...
3 An explicit specialization may be declared for a function template, a class template, a member of a class
template or a member template. An explicit specialization declaration is introduced by template<>. In
an explicit specialization declaration for a class template, a member of a class template or a class member
template, the name of the class that is explicitly specialized shall be a simple-template-id. In the explicit specialization declaration for a function template or a member function template, the name of the function
or member function explicitly specialized may be a template-id.
[Example:
template<class T = int> struct A {
static int x;
};
template<class U> void g(U) { }
template<> struct A<double> { }; // specialize for T == double
template<> struct A<> { }; // specialize for T == int
template<> void g(char) { } // specialize for U == char
// U is deduced from the parameter type
template<> void g<int>(int) { } // specialize for U == int
template<> int A<char>::x = 0; // specialize for T == char
template<class T = int> struct B {
static int x;
};
template<> int B<>::x = 1; // specialize for T == int
— end example ]
1)Sorry, I can't find a better standard cite / example. It doesn't cover exactly the OP's case, but demonstrates at least the same, that templates still need the <> angle brackets to be identified as templates (classes or functions).
possible workaround if it's important to disguise the nature of your object:
// the template class
template<int n=0>
class ReallyA {};
// an alias for the common case
using A = ReallyA<0>;
A a;
This is how the standard library defines std::string, for example.
using string = basic_string<char>;
Let's consider this code:
template <bool c>
class A {
public:
A() = default;
// I want to enable f(int) only if c == true
template<typename Temp = typename enable_if<c>::type>
void f(int val) {
cout << val << endl;
};
};
int main() {
A<false> a;
A<true> b;
b.f(543);
}
When I try to compile this I get the following error:
error: no type named 'type' in 'struct std::enable_if<false, void>'
But I don't use the template method f(int) when the argument <bool c> is false then it shouldn't exist.
The compiler does not "instantiate" your template, as you seem to incorrectly believe. The compiler is simply trying to parse and analyze your template declaration, which is a part of your class definition. If the class is instantiated, then all member declarations have to be valid. Your member template declaration is not valid. Hence the error.
If some template is "not used", it means that it does not get specialized and instantiated. But the declaration of that template still has to be valid. And the validity of those parts of that declaration that do not depend on template parameters is checked immediately. In other words, what you wrote in your code is no different from
template <typename T = jksgdcaufgdug> void foo() {}
int main() {}
or, closer to your situation
template <typename T = std::enable_if<false>::type> void foo() {}
int main() {}
Even though these programs do not "use" (do not instantiate) function template foo, it still does not mean that the declaration of foo can contain random garbage like jksgdcaufgdug or explicitly refer to non-existent entities like std::enable_if<false>::type. The above examples will not compile for that reason.
You can use "random garbage" in dependent contexts, like
template <typename T> void foo(typename T::kjhdfjskhf x)
{
typename T::jksgdcaufgdug i;
}
and you can use std::enable_if in dependent contexts like
template <typename T,
typename U = typename enable_if<is_void<T>::value>::type>
void bar()
{
}
and it will not produce "early" errors, but in your case enable_if<c> does not depend on Temp, so it is not in dependent context. Which means that the correctness of typename enable_if<c>::type is checked immediately when you instantiate A<false>.
While writing a small template metaprogramming library for personal use, I came across an interesting problem.
Since I was reusing a few partial specializations for some metafunctions, I decided I would put them under a common template class and use tags along with nested partial specialization to provide the differences in behaviour.
The problem is I am getting nonsensical (to me) results. Here is a minimal example that showcases what I am trying to do:
#include <iostream>
#include <cxxabi.h>
#include <typeinfo>
template <typename T>
const char * type_name()
{
return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
}
template <typename... Args>
struct vargs {};
namespace details
{
template <typename K>
struct outer
{
template <typename Arg>
struct inner
{
using result = Arg;
};
};
}
struct tag {};
namespace details
{
template <>
template <typename Arg, typename... Args>
struct outer<tag>::inner<vargs<Arg, Args...>>
{
using result = typename outer<tag>::inner<Arg>::result;
};
}
template <typename T>
using test_t = typename details::outer<tag>::inner<T>::result;
int main()
{
using t = test_t<vargs<char, int>>;
std::cout << type_name<t>() << '\n';
return 0;
}
I am getting vargs<char, int> as output when using the 5.1.0 version of gcc and tag when using the 3.6.0 version of clang. My intention was for the above piece of code to print char so I am pretty baffled by these results.
Is the above piece of code legal or does it exhibit undefined behavior?
If it's legal what is the expected behavior according to the standard?
Your code is correct; out-of-class implicitly instantiated class template member class template partial specializations are intended to be allowed by the Standard, as long as they are defined early enough.
First, let's try for a minimal example - noting by the way that there's nothing here that requires C++11:
template<class T> struct A {
template<class T2> struct B { };
};
// implicitly instantiated class template member class template partial specialization
template<> template<class T2>
struct A<short>::B<T2*> { };
A<short>::B<int*> absip; // uses partial specialization?
As noted elsewhere MSVC and ICC use the partial specialization as expected; clang selects the partial specialization but messes up its type parameters, aliasing T2 to short instead of int; and gcc ignores the partial specialization entirely.
Why out-of-class implicitly instantiated class template member class template partial specialization is allowed
Put simply, none of the language that permits other forms of class template member class template definitions excludes out-of-class implicitly instantiated class template member class template partial specialization. In [temp.mem], we have:
1 - A template can be declared within a class or class template; such a template is called a member template. A
member template can be defined within or outside its class definition or class template definition. [...]
A class template partial specialization is a template declaration ([temp.class.spec]/1). In the same paragraph, there is an example of out-of-class nonspecialized class template member class template partial specialization ([temp.class.spec]/5):
template<class T> struct A {
struct C {
template<class T2> struct B { };
};
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
struct A<T>::C::B<T2*> { };
A<short>::C::B<int*> absip; // uses partial specialization
There is nothing here to indicate that the enclosing scope cannot be an implicit specialization of the enclosing class template.
Similarly, there are examples of in-class class template member class template partial specialization and out-of-class implicitly instantiated class template member class template full specialization ([temp.class.spec.mfunc]/2):
template<class T> struct A {
template<class T2> struct B {}; // #1
template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1
(clang (as of 3.7.0-svn235195) gets the second example wrong; it selects #2 instead of #3 for absip.)
While this does not explicitly mention out-of-class implicitly instantiated class template member class template partial specialization, it does not exclude it either; the reason it isn't here is that it's irrelevant for the particular point being made, which is about which primary template or partial template specializations are considered for a particular specialization.
Per [temp.class.spec]:
6 - [...] when the primary
template name is used, any previously-declared partial specializations of the primary template are also
considered.
In the above minimal example, A<short>::B<T2*> is a partial specialization of the primary template A<short>::B and so should be considered.
Why it might not be allowed
In other discussion we've seen mention that implicit instantiation (of the enclosing class template) could result in implicit instantiation of the definition of the primary template specialization to take place, resulting in an ill-formed program NDR i.e. UB; [templ.expl.spec]:
6 - If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]
However, here the class template member class template is not used before it is instantiated.
What other people think
In DR1755 (active), the example given is:
template<typename A> struct X { template<typename B> struct Y; };
template struct X<int>;
template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
int k = X<int>::Y<int*>().n;
This is considered problematic only from the point of view of the existence of the second line instantiating the enclosing class. There was no suggestion from the submitter (Richard Smith) or from CWG that this might be invalid even in the absence of the second line.
In n4090, the example given is:
template<class T> struct A {
template<class U> struct B {int i; }; // #0
template<> struct B<float**> {int i2; }; // #1
// ...
};
// ...
template<> template<class U> // #6
struct A<char>::B<U*>{ int m; };
// ...
int a2 = A<char>::B<float**>{}.m; // Use #6 Not #1
Here the question raised is of precedence between an in-class class template member class template full specialization and an out-of-class class template instantiation member class template partial specialization; there is no suggestion that #6 would not be considered at all.
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;
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>.