This doesn't compile:
template<class X> struct A {
template<int I> void f() {}
};
template<class T> void g()
{
A<T> a;
a.f<3>(); // Compilation fails here (Line 18)
}
int main(int argc, char *argv[])
{
g<int>(); // Line 23
}
The compiler (gcc) says:
hhh.cpp: In function 'void g()':
hhh.cpp:18: error: expected primary-expression before ')' token
hhh.cpp: In function 'void g() [with T = int]':
hhh.cpp:23: instantiated from here
hhh.cpp:18: error: invalid use of member (did you forget the '&' ?)
Can anyone explain why this is? Is there a way to get it to work?
Try the following code:
template<class T> void g()
{
A<T> a;
a.template f<3>(); // add `template` keyword here
}
According to C++'03 Standard 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.
Future C++ Standard seems to be still require this keyword according to draft n2857 14.3/4. Some compilers has special mode that allows to compile original code without errors (Comeau compiles it in so called relaxed mode).
Related
This doesn't compile:
template<class X> struct A {
template<int I> void f() {}
};
template<class T> void g()
{
A<T> a;
a.f<3>(); // Compilation fails here (Line 18)
}
int main(int argc, char *argv[])
{
g<int>(); // Line 23
}
The compiler (gcc) says:
hhh.cpp: In function 'void g()':
hhh.cpp:18: error: expected primary-expression before ')' token
hhh.cpp: In function 'void g() [with T = int]':
hhh.cpp:23: instantiated from here
hhh.cpp:18: error: invalid use of member (did you forget the '&' ?)
Can anyone explain why this is? Is there a way to get it to work?
Try the following code:
template<class T> void g()
{
A<T> a;
a.template f<3>(); // add `template` keyword here
}
According to C++'03 Standard 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.
Future C++ Standard seems to be still require this keyword according to draft n2857 14.3/4. Some compilers has special mode that allows to compile original code without errors (Comeau compiles it in so called relaxed mode).
Consider the code:
class Test {
public:
template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }
template<int N> void bar() {}
template<> int bar<0>() { return 7; }
};
I have tested the code with different compilers (through Compiler Explorer).
In case of Clang 7.0.0 foo compiles, while bar is giving an error:
:8:20: error: no function template matches function template
specialization 'bar'
template<> int bar<0>() { return 7; }
^
:7:26: note: candidate template ignored: could not match 'void
()' against 'int ()'
template<int N> void bar() {};
^
Visual C++ agrees (MSVC 19 2017 RTW):
(8): error C2912: explicit specialization 'int
Test::bar(void)' is not a specialization of a function template
gcc 8.2 does not compile any of the code (though the reason is probably a bug in C++17 support:
:5:14: error: explicit specialization in non-namespace scope
'class Test'
template<> auto foo<0>() { return 7; };
^
:5:28: error: template-id 'foo<0>' in declaration of primary
template
template<> auto foo<0>() { return 7; };
^
:7:26: error: too many template-parameter-lists
template<int N> void bar() {};
^~~
:8:14: error: explicit specialization in non-namespace scope
'class Test'
template<> int bar<0>() { return 7; }
^
:8:20: error: expected ';' at end of member declaration
template<> int bar<0>() { return 7; }
^~~
;
:8:23: error: expected unqualified-id before '<' token
template<> int bar<0>() { return 7; }
^
What is the correct interpretation here? Can I have a different return type for different method specializations (and why only with auto, but not while specifying them explicitly)? With my limited understanding of auto and templates I would go with saying "no". I don't understand why would using auto instead of explicitly naming the return type allow to have different return type for different specializations.
However, those codes are simplified versions of the code that I have found elsewhere, so maybe my interpretation is incorrect - and in that case I would be grateful for the explanation why different return type is allowed when auto is used for specialization, while explicitly naming the type seems to be forbidden.
There are several different problems with the example code.
1) GCC fails to implement CWG 727 (required in C++17): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 which causes error: explicit specialization in non-namespace scope 'class Test'
2) If we ignore that, the example code can be simplified to just
template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }
template<int N> void bar() {}
template<> int bar<0>() { return 7; }
which still exhibits the same errors. Now all compilers agree on the output. They compile foos and error out on the bar specialization.
why different return type is allowed when auto is used for specialization
It is allowed by a standard to specialize functions with auto return type if the specializations also have auto placeholder
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type
http://eel.is/c++draft/dcl.spec.auto#11
So by virtue of this specialization conforming to the standard (being similar to one of the given examples) and not being explicitly forbidden anywhere it's allowed.
As for the error with bar, the standard says that return type is a part of function template signature:
⟨function template⟩ name, parameter type list ([dcl.fct]), enclosing namespace (if any), return type, template-head, and trailing requires-clause ([dcl.decl]) (if any)
http://eel.is/c++draft/defns.signature.templ
As such, in compiler's eyes template<> int bar<0>() { return 7; } is a specialization of template<... N> int bar(); template (notice the return type). But it wasn't declared previously (specialization can't precede declaration) so compilation fails! If you add template<int N> int bar(); then it'll compile (but complain about call ambiguity if you try to call bar).
Basically, you can't change the function signature in a specialization, you can only specialize (duh) template parameters (which should also be exactly the same as in declaration, since it's also part of the signature).
Can I have a template specialization with explicitly declared return type different from the base template at all
As explained - you can't change the function template signature which means you can't change the return type. BUT, you can specialize the return type if it depends on a template parameter!
Consider
template<int N, typename R = void> R bar() {}
template<> int bar<0>() { return 7; }
// bar<0, int> is deduced, see: http://eel.is/c++draft/temp.expl.spec#10
This is allowed but it has a disadvantage of having to write bar<0, int> when you want to make a call to a specialization: https://godbolt.org/z/4lrL62
This can be worked around by making the type conditional in the original declaration: https://godbolt.org/z/i2aQ5Z
But it'll quickly become cumbersome to maintain once the number of specializations grows.
Another, perhaps a bit more maintainable option, would be to return something like ret_type<N>::type and specialize that along with bar. But it's still won't be as clean as with just using auto.
This doesn't compile:
template<class X> struct A {
template<int I> void f() {}
};
template<class T> void g()
{
A<T> a;
a.f<3>(); // Compilation fails here (Line 18)
}
int main(int argc, char *argv[])
{
g<int>(); // Line 23
}
The compiler (gcc) says:
hhh.cpp: In function 'void g()':
hhh.cpp:18: error: expected primary-expression before ')' token
hhh.cpp: In function 'void g() [with T = int]':
hhh.cpp:23: instantiated from here
hhh.cpp:18: error: invalid use of member (did you forget the '&' ?)
Can anyone explain why this is? Is there a way to get it to work?
Try the following code:
template<class T> void g()
{
A<T> a;
a.template f<3>(); // add `template` keyword here
}
According to C++'03 Standard 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.
Future C++ Standard seems to be still require this keyword according to draft n2857 14.3/4. Some compilers has special mode that allows to compile original code without errors (Comeau compiles it in so called relaxed mode).
This seems to be another "who's doing it well?" question since gcc 6.0.0 and clang 3.7.0 behaves different.
Let's suppose we have a variable template which takes a const char * as non template argument and is specialized for a given pointer:
constexpr char INSTANCE_NAME[]{"FOO"};
struct Struct{ void function() const { std::cout << __PRETTY_FUNCTION__; } };
std::ostream &operator <<(std::ostream &o, const Struct &) { return o << INSTANCE_NAME; }
template <const char *> char Value[]{"UNKNOWN"};
// spezialization when the pointer is INSTANCE_NAME
template < > Struct Value<INSTANCE_NAME>{};
Note that the template variable have different types depending on the specialization. Ten we have two template functions, each of one takes a const char * as non template argument and forwards it to the variable template:
template <const char *NAME> void print()
{
std::cout << Value<NAME> << '\n';
}
template <const char *NAME> void call_function()
{
Value<NAME>.function();
}
Then, calling this functions results in different behaviours:
int main()
{
print<INSTANCE_NAME>();
call_function<INSTANCE_NAME>();
return 0;
}
Code Here
clang 3.7.0 prints FOO and void Struct::function() const (as I was expecting) while gcc 6.0.0 fails to compile with the error below:
request for member 'function' in 'Value', which is of non-class type 'char [8]'
I'm almost sure that gcc failed to forward the template non-type argument NAME to the variable template Value in the function call_function and for this reason it selects the unspecialized variable template which is the one with 'char [8]' type...
It is acting like it is copying the template argument. This only happens when calling a member function of the object, if we comment the body of call_function, the output is FOO not UNKNOWN, so in the print function the forwarding is working even in gcc.
So
What's the correct behaviour? (mi bet is for clang)
How can I open a bug ticket for the compiler who's doing it wrong?
The interesting thing is that GCC is even self-contradictory in this example.
Lets declare an incomplete template class which should give is some nice compiler messages that we can abuse:
template <typename T>
struct type_check;
We'll also make another const char* that we can use for testing:
constexpr char NOT_FOO[]{"NOT_FOO"};
Now we'll see what the compiler chokes on:
template <const char *NAME> void foo()
{
type_check<decltype(Value<FOO>)> a;
type_check<decltype(Value<NAME>)> b;
type_check<decltype(Value<NOT_FOO>)> c;
type_check<decltype(Value<FOO>.foo())> d;
type_check<decltype(Value<NAME>.foo())> e;
type_check<decltype(Value<NOT_FOO>.foo())> f;
}
Here are the errors which GCC 5.1.0 produces (edited a bit for clarity):
test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type
type_check<decltype(Value<FOO>)> a;
^
test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type
type_check<decltype(Value<NAME>)> b;
test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type
type_check<decltype(Value<NOT_FOO>)> c;
^
test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type
type_check<decltype(Value<FOO>.foo())> c;
test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’
type_check<decltype(Value<NAME>.foo())> d;
test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’
type_check<decltype(Value<NOT_FOO>.foo())> f;
Let's take these one at a time.
Error 1:
test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type
type_check<decltype(Value<FOO>)> a;
In the first error, we can see that GCC correctly deduces that the type of Value<FOO> is Foo. This is what we expect.
Error 2:
test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type
type_check<decltype(Value<NAME>)> b;
Here, GCC correctly does the forwarding and works out that Value<NAME> is of type Foo.
Error 3:
test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type
type_check<decltype(Value<NOT_FOO>)> c;
Great, Value<NOT_FOO> is "UNKNOWN", so this is correct.
Error 4:
test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type
type_check<decltype(Value<FOO>.foo())> c;
This is fine, Value<FOO> is Foo, which we can call foo on, returning void.
Error 5:
test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’
type_check<decltype(Value<NAME>.foo())> d;
This is the odd one. Even though in error 2 we can see that GCC knows that the type of Value<NAME> is Foo, when it tries to do the lookup for the foo function, it gets it wrong and uses the primary template instead. This could be some bug in the function lookup which doesn't correctly resolve the values of non-type template arguments.
Error 6:
test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’
type_check<decltype(Value<NOT_FOO>.foo())> f;
Here we can see the compiler correctly choose the primary template when working out what Value<NOT_FOO> is. The thing that interests me is the (const char*)(& NOT_FOO)) which GCC deduces as the type of NOT_FOO. Maybe this is a pointer to the issue? I'm not sure.
I would suggest filing a bug and pointing out the discrepancy. Maybe this doesn't fully answer your question, but I hope it helps.
There is a reasonable consensus that variable template specializations are permitted to alter the type of the variable template: C++1y/C++14: Variable Template Specialization?
The behavior of gcc is particularly interesting if the default type of Value is changed to a type with a function method:
struct Unknown{ void function() const { std::cout << __PRETTY_FUNCTION__; } };
template <const char *> Unknown Value;
prog.cc: In instantiation of 'void call_function() [with const char* NAME = ((const char*)(& INSTANCE_NAME))]':
prog.cc:26:18: required from here
prog.cc:20:5: error: 'Unknown::function() const' is not a member of 'Struct'
Value<NAME>.function();
^
The bug appears to be that where the non-specialized variable template has a type that is not dependent on the variable template template parameters, gcc assumes within template methods that use that variable template that the variable template always has that type.
The workaround, as usual, is to unconditionally forward the variable template to a class template with specialization(s) of the class template, and with the necessary fiddling for ODR compliance.
Another (possibly easier) workaround is to make the non-specialized variable template type somehow dependent on the variable template template parameters; in your case this would work:
template <const char *P> decltype(*P) Value[]{"UNKNOWN"};
I can't find a corresponding issue in gcc bugzilla so you might want to enter a new one. Here's a minimal example:
struct U { void f() {} };
struct V { void f() {} };
template<class T> U t;
template<> V t<int>;
template<class T> void g() { t<T>.f(); }
int main() { g<int>(); }
template<class T> struct A {
typedef int Int;
A::Int b; // Line 1 (fails)
Int c; // Line 2 (compiles)
};
int main(){
A<int> x;
x.c = 13;
}
Errors
error: ISO C++ forbids declaration of ‘Int’ with no type
error: extra qualification ‘A<T>::’ on member ‘Int’
error: expected ‘;’ before ‘b’
Line 1 fails but Line 2 compiles. Why?
You need a typename
typename A::Int b;
The typename keyword is required because the member is referred to using a qualified name A::Int.
Int c is fine because no qualified name is used in that case.
14.6/6
Within the definition of a class template or within the definition of a member of a class template, the keyword typename is not required when referring to the unqualified name of a previously declared member
of the class template that declares a type. The keyword typename shall always be specified when the
member is referred to using a qualified name, even if the qualifier is simply the class template name.