Prevent implicit instanciation in class template - c++

I'm trying to declare class B such as written below. The problem is, as far as I know, A<B, T1> is actually implicitly considered as A<B<T1>, T1> which doesn't match class A definition. Therefore, my question is: is it possible to use class B inside its own definition, in its "template-non-instanciated" form? And if so how?
template <template <class T1> class T2, class T1>
class A {
};
template <class T1>
class B {
A<B, T1>* example;
};
which results in the following compilation error with gcc4.2 :
error: type/value mismatch at argument 1 in template parameter list for 'template < template < class T1 > class T2, class T1 > class A'
error: expected a class template, got 'B< T1 >'

This seems to be a bug in g++-4.2 as I can reproduce your problem with that compiler.
If you can move to at least 4.4 your code will compile as-is. Alternately you can write A< ::B, T1>* example; to force it to use the global scope template, which compiles in 4.2.

Related

Why injected-class-name is sometimes not treated as a template name in a class template?

Source
In the following cases, the injected-class-name is treated as a template-name of the class template itself:
it is followed by <
it is used as a template argument that corresponds to a template template parameter
it is the final identifier in the elaborated class specifier of a friend class template declaration.
So I tried to examine all 3 cases (additionally in context of base ambiguity though I think it shouldn't matter here).
The first case seems simple.
The question is - why don't commented out examples work? They don't on both GCC & Clang so I don't think it's an implementation issue
template <template <class> class> struct A;
template <class T> struct Base {};
template <class T> struct Derived: Base<int>, Base<char>
{
// #1
typename Derived::Base<double> d;
// #2
// using a = A<Base>;
using a = A<Derived::template Base>;
// #3
template<class U1>
friend struct Base;
// template<class U>
// friend struct Derived::template Base;
};
Are the rules above only for template itself, not for bases? If so, what are rules for bases, especially for the last 2 of the cases?
The relevant rule here is [temp.local]/4:
A lookup that finds an injected-class-name ([class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is used as a template-name, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [ Example:
template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
typename Derived::Base b; // error: ambiguous
typename Derived::Base<double> d; // OK
};
— end example ]
Which I think means that this is a gcc and a clang bug. In:
using a = A<Base>;
All of the injected-class-names that are found do refer to specializations of the same class template (Base<T>) and the name is used as a template-name (because it's a template argument for a template template parameter), so this should just refer to the class template itself and not be ambiguous.

Why is a class/struct declaration with different number of template parameters not allowed?

template <class T1, class T2>
class A {};
template <class T1>
class A {};
A<int, int> a;
A<int> b;
This code generates
error C2976: 'A' : too few template arguments
at the second diclaration of 'A' class.
Your first declaration defines a class A with 2 template arguments. Anything after that must either be a specialization of that or some other enabled version.
If you want to allow either 1 or 2 template arguments you can use variadic templates as follows:
template <class... Args>
class A;
template <class T1, class T2>
class A<T1, T2> {};
template <class T1>
class A<T1> {};
Live demo
There is no SFINAE going on here. You are redeclaring the class A, originally with 2 template parameters, to another one with only 1 template parameters, and hence the error. g++ gives a more explicit error:
error: redeclared with 1 template parameter class A {};
note: previous declaration 'template class A' used 2 template parameters
SFINAE is about a substitution failure; i.e. the problem must be a consequence of what types you are placing as template parameters. Not every error qualifies... as an extreme example consider
template<T>
struct foo {
!##*&!%^#
};
this is also an error, but not a substitution failure :-)

c++ function argument, automatic upcasting of multiple templated class

I have a function taking, as an argument, a template class with multiple parameters (see 'func2' in the following code). I'd like the function to be able to take as argument a class inheriting from the agument type, and automatically resolve template types by upcasting. In the following example, it's ok for function 'func1', but it's apparently not possible straightforwardly when argument is a multiple template class as for function 'func2'.
The error message states: "Candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter"
I understand (and kind of agree) with the message. But this is the same case for 'func1', and it's working fine.
So my question is, is there a way I can treat variable 'd' as type 'C' by automatic upcasting when using a function ? And if so, how ?
I'm using Xcode 5.1.1, clang-503.0.40. and C++11 option. Thanks
template <class T1>
class A {};
class B : public A<int> {};
template <template <class T1> class T, class T1 >
void func1(T<T1> _arg) {}
template <class T1, class T2>
class C {};
template <class T1>
class D : public C<T1,int> {};
template <template <class T1, class T2> class T, class T1, class T2>
void func2(T<T1,T2> _arg) {}
int main() {
A<int> a;
B b;
func1(a);//works
func1(b);//works, T1 is resolved
C<float,int> c;
D<float> d;
func2(c);//works
func2(d);//doesn't work,compilation error message: "Candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter"
return 0;
}
No, there's no way to do what you're trying to do.
You can use an alias though, assuming your compiler supports them:
template < typename T >
using D = C<T,int>;
I believe that should match your function template just fine.
If you're trying to have D have different behavior then you'd create a partial specialization of C.
Ok so the best option I found is as follow. The idea is to make 'D' believe it's a multiple template class, but in fact one of the template parameters is only meant to be a specific type (int). This way, 'D2' is sort of both a one template class and two template class. There's still one partial specialization when defining template class D , but it's actually only the normal definition of class D.
template <class T1, class T2>
class C {};
template <class T1, class T2=int>
class D;
template <class T1>
class D<T1,int> : public C<T1,int> {};
template <typename T1>
using D2 = D<T1>;
template <template <class T1, class T2> class T, class T1, class T2>
void func2(T<T1,T2> _arg) {}
int main()
{
C<float,int> c;
D2<float> d;
func2(c);//works
func2(d);//works
//instantiation tests:
D<float> test1;//works -> normal
D<float, int> test2;//works -> not ideal but well ok
D<float, float> test3;//doesn't work -> also normal. States: "Implicit instantiation of undefined template 'D<float, float>' "
return 0;
}

template method and default template argument

My problem can be resumed by the following piece of code:
template <typename T> struct C2;
template <typename T>
struct C1
{
template <typename Type,
template <typename Ti> class Container = C2>
void m() {}
};
template <typename T>
struct C2
{
template <typename Type = int,
template <typename Ti> class Container = C2> // <-- Here is the problem!
void m() {}
};
The gnu compiler, version 4.8.1 fails with the following message:
test-temp.C:16:47: error: invalid use of type ‘C2<T>’ as a default value for a template template-parameter
template <typename Ti> class Container = C2>
It refers to default template parameter C2 for the the method C2::m.
Apparently (it is my opinion), the compiler is seeing C2<T> as default parameter instead of C2 (without <T>). So, when it finds the instruction it fails because type C2<T> does not match with Container.
However, clang++, just for exactly the same code, compiles fine!
My questions:
Which compiler has the truth?
Is there some alternative for expressing the same sense with the current version of gnu compiler?
Thanks in advance
Leandro
I think Clang is correct, and g++ is in error, quote from the draft Standard (bold emphasis is mine)
14.6.1 Locally declared names [temp.local]
1 Like normal (non-template) classes, class templates have an
injected-class-name (Clause 9). The injectedclass-name can be used as
a template-name or a type-name. When it is used with a
template-argument-list, as a template-argument for a template
template-parameter, or as the final identifier in the
elaborated-typespecifier of a friend class template declaration, 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 <>.
You can use the :: scope resolution operator to beat g++ into submission
template <typename T>
struct C2
{
template <typename Type = int,
template <typename Ti> class Container = ::C2>
// ^^ <-- here is the solution!
void m() {}
};
Live Example.
So does the 14.6.1 reference in TemplateRex's answer mean that G++ is correct (and Clang and VC++ are wrong) to accept this, since it uses X as the template argument to a template template parameter?
template< template< typename > class T >
class factory { };
template< typename T >
class X
{
friend class factory< X >; // ***
};
int main()
{
}
In this example G++ treats X as the name of the class template, whereas Clang and VC++ treat it as the injected class name.
Edit: Clang 5.0.0 now accepts the code, the same as G++ and EDG.

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;