I noticed a different behavior between gcc 9.2.0 and clang++ 9.0.1. My code is as follows
//header.hh
...
template <typename T>
class Outer {
...
public:
template <typename S>
class Inner;
...
};
template <typename T>
template <typename S>
class Inner {
...
Inner& func();
...
};
then, since the function func() is implemented in another file
//implementation.cc
template <typename T>
template <typename S>
Outer<T>::Inner<S>& Outer<T>::Inner<S>::func() {
...
};
Now, if I use g++ the compilation is OK. If I use clang++ I get
src/implementation.cc:6:1: error: missing 'typename' prior to dependent type template name 'Outer<T>::Inner'
Outer<T>::Inner<S>& Outer<T>::Inner<S>::func() {
^
1 error generated.
However if I follow its suggestion and use
typename Outer<T>::Inner<S>& Outer<T>::Inner<S>::func()
I got another error:
src/implementation.cc:6:21: error: use 'template' keyword to treat 'Inner' as
a dependent template name typename Outer<T>::Inner<S>& Outer<T>
::Inner<S>::func() {
And now its suggestion seems very weird.
QUESTIONS
Why the two compiler are behaving differently?
What is the correct syntax to use?
The correct syntax would be the following:
template <typename T>
template <typename S>
typename Outer<T>::template Inner<S> &Outer<T>::Inner<S>::func() {
...
}
You can find the full explanation for this syntax in this question.
However, a simpler and also valid syntax would be this:
template <typename T>
template <typename S>
auto Outer<T>::Inner<S>::func() -> Inner& {
...
}
By using the trailing return type syntax in the example above you can take advantage of the fact that the name resolution scope is within Outer<T>::Inner<S> at that point so you can use the injected class name of the Inner.
Related
Consider the following code:
template <typename T>
struct X
{
void foo(){}
};
class Y { };
template <typename T, typename... U>
class Example {
protected:
template <template<typename> typename example_t>
using alias = X<example_t<T>>;
};
template <typename T>
struct ExampleA : public Example<T, Y>
{
using MyAlias = ExampleA::alias<ExampleA>;
};
In C++14 I could do the following and have it work as expected:
ExampleA<int>::MyAlias test;
test.foo();
A recent upgrade to C++17 now gives a warning 'ExampleA<T>::alias': dependent name is not a type as well as syntax error: identifier 'alias'.
Normally when you get something involving a dependent name it means you need to add the typename keyword as in the following example (iterator is dependent upon std::vector<T>):
template<typename T>
void bar() {
/* typename */ std::vector<T>::iterator it;
}
However, I don't believe that to be the case here. Also, using using MyAlias = Example<T, Y>::alias<ExampleA>; results in the same error.
Did a change in C++17 invalidate this code, or is this a compiler bug? What can I do to fix this in C++17?
MSVC ignored the abscence of desambiguators due to an implementation detail of their compiler.
With new advances and reengineering of their compiler, they now implement two phase name lookup as they should. However, with that implemented, it's really hard in some cases to ignore absence of desambiguators.
They became even more strict with the /permissive- flag, which is an attempt to disable most of their extensions due to their previous lack of two phase name lookup.
Your code snippet look like this with desambiguators:
template <typename T>
struct ExampleA : public Example<T, Y>
{
using MyAlias = typename ExampleA::template alias<ExampleA>;
};
See it as an opportunity to upgrade the compilance and portability of your code.
However with C++20, many cases where desambigurator were needed are now optional:
template<typename T>
auto wrapper() -> T::type; // no typename!
We reduced a portion of code we cannot find the right syntax for to a minimal example.
Let's assume the following definitions (worry not about the "why" ;)
template <class>
class Element
{};
template <template <class> class>
class Client
{};
template <class>
struct TemplatedProvider
{
template <class T>
using element_template = Element<T>;
};
Now, with C++11 onward, we can use either a class template or a type alias template to instantiate the Client template. The following function compiles just fine:
void fun()
{
Client<Provider::element_template> client;
Client<TemplatedProvider<int>::element_template> clientBis;
}
But we cannot find the proper syntax in the following case, when the template argument given to Client is a dependent name:
template <class T>
void templatedFun()
{
Client<TemplatedProvider<T>::element_template> client;
}
Clang (tested with 3.6) is emitting the following compilation error:
template argument for template template parameter must be a class template or type alias template
Can we fix this syntax ?
It has to be:
template <class T>
void templatedFun()
{
Client<TemplatedProvider<T>::template element_template> client;
}
You can use the template keyword:
template <class T>
void templatedFun()
{
Client<TemplatedProvider<T>::template element_template> client;
}
See this question for detailed discussion on the template and typename keywords.
I need to use a template class which is defined in another template class as parameter of another template as return value in template method. I know it sounds complicated, code below explains it better. Problem is that the code cannot be compiled, it ends with following error:
type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
expected a class template, got 'CDummy<T2>::Policy2'
but I'm pretty sure that given class fulfills needs. Problem is that the method, which uses it, is template too and so compiler does not know what exactly CDummy<T2>::Policy2 is. If the Policy2 would not be template, but regular class or if I could fill its argument, I would use typename which would tell the compiler not to worry about it, but how can this be done with template?
// I cannot change this interface - it's given by a library
template <class T, template <class> class Policy>
class Result : public Policy<T>
{
T data;
};
template <class T>
class Policy1
{
};
// I use this for allowing Policy2 to change behaviour according Dummy
// while it keeps template interface for class above
template <class Dummy>
class CDummy
{
public:
template <class T>
class Policy2 : public Policy1<T>
{
};
};
// Both variables are created ok
Result<int, Policy1 > var1;
Result<int, CDummy<float>::Policy2 > var2;
// This is ok, too
template <class T>
Result<T, Policy1 > calc1()
{
return Result<int, Policy1>();
}
// But this ends with the error:
// type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
// expected a class template, got 'CDummy<T2>::Policy2'
template <class T1, class T2>
Result<T1, CDummy<T2>::Policy2 > calc2() // <-- Here is the generated error
{
typedef typename DummyTypedef CDummy<T2>;
return Result<T1, DummyTypedef::Policy2>();
}
Notes:
I use gcc 4.7.3 32bit in GNU/Linux Ubuntu 13.04. 32 bit.
For various reasons, I cannot use C++11 standard (yet) and so I cannot use template typedefs
I believe that the name CDummy<T2>::Policy2 is a dependent name in that context and that you should use the template keyword to inform the compiler that it is indeed a template.
template <class T1, class T2>
Result<T1, CDummy<T2>::template Policy2 > calc2() // <-- Here is the generated error
// ^^^^^^^^
additionally the implementation of that same function seems to be wrong also. The order of typedefs is original name, new name, and CDummy<T2> is known to be a type (i.e. there is no need for the typename):
typedef CDummy<T2> DummyTypedef;
The return statement would then be:
return Result<T1, DummyTypedef::template Policy2>();
I'm experiencing a problem in some code I've been working on. Here is the most simplified version of it I could create:
template <class A>
class Outer {
public:
template <class B>
class Inner {
public:
template <class C>
void foo(Outer<C>::Inner<C> innerC) { }
};
Inner<A> inner;
};
class X {};
class Y {};
int main() {
Outer<X> outerX;
Outer<Y> outerY;
outerX.foo<Y>(outerY.inner);
}
The error is:
error: expected primary-expression before ">" token
and is triggered at compiletime at the declaration of void foo. What is incorrect about my code that is making this happen?
In words, what I am trying to do is have the nested class be able to take in a nested class with any template type - but of course the nested class's template type depends on the outer class's template type, so I use the :: syntax, but that gives an error.
I understand that what I'm trying to do here might not be a good practice, but the purpose of this question is to understand template syntax better.
There is no conversion from 1 to Inner<C>. Is that an error in your reduced test case, or is it supposed to be:
template <class C>
void foo(C innerC) { }
Update: After the code was fixed, it can be seen that the problem is the lack of template before Inner<C>. Otherwise the compiler will assume that Inner is a value.
template <class C>
void foo(Outer<C>::template Inner<C> innerC) { }
I'm trying to compile some Microsoft Visual C++ code using g++. Now I've come across a compiler error which I really can't understand. The (simplified) code looks like this:
template<int X> struct A {
template<class Ret> static Ret call() {
return 0;
}
};
template<int X> struct B : A<X> {
int f() {
return A<X>::call<int>();
}
};
When I try to compile this with g++ (version 4.4.5), I get the following error:
main.cpp: In member function int B<X>::f():
main.cpp:16: error: expected primary-expression before int
main.cpp:16: error: expected ; before int
main.cpp:16: error: expected unqualified-id before > token
If I remove the template type (Ret) from method A::call, the code compiles just fine. Can anybody see whats wrong here?
Thanks!
You need the template keyword:
return A<X>::template call<int>();
call is a dependent name, meaning that its signification depends on a template parameter, which is not known when the compiler process f(). You need to indicate that call is a function template by prefixing it with the template keyword.
The same thing happens when you try to access a nested type: you need to add the typename keyword to indicate that the name denotes a type:
template <typename T>
struct A { typedef int type; };
template <typename T>
void f()
{
typename A<T>::type i = 0; // notice "typename" here
}
And sometimes you even need to mix both:
template <typename T>
struct A
{
template <typename U>
struct inner { };
};
template <typename T>
void f()
{
typename A<T>::template inner<int> var;
}
The use of these two keywords is thoroughly explained in the answers to this question: Where and why do I have to put the “template” and “typename” keywords? (thanks to #Björn Pollex for finding the link).
A is a template, and without knowing X the compiler can't determine the contents of A<X>. Especially it doesn't know that call will end up being a template.
To tell that to the compiler you have to use the template keyword:
template<int X> struct B : A<X> {
int f() {
return A<X>::template call<int>();
}
};
You have to specify that the function you're calling is a template, as it's part of a template class. The compiler is not aware that any given A<X> has a template function named call, and therefore you need to help it.
template<int X> struct B : A<X> {
int f() {
return A<X>::template call<int>();
}
};