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 code fails to compile on g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, with this error
test.cpp: In function ‘T mul(V&, V&)’:
test.cpp:38:27: error: expected primary-expression before ‘>’ token
test.cpp:38:29: error: expected primary-expression before ‘)’ token
test.cpp:38:53: error: expected primary-expression before ‘>’ token
test.cpp:38:55: error: expected primary-expression before ‘)’ token
but it compiles and executes correctly on Microsoft C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
#include <iostream>
#include <complex>
template <class T>
class SM
{
public:
T value;
};
template <class T>
class SC : public SM<T>
{
};
class PSSM {
public:
template <class T>
T & getSC() { return sc; }
private:
SC<double> sc;
};
class USSM {
public:
template <class T>
T & getSC() { return sc; }
private:
SC<std::complex<double> > sc;
};
template <class T, class V>
T mul( V & G, V & S) {
return (G.getSC<SC<T> >().value * S.getSC<SC<T> >().value); // error is here
}
int main() {
PSSM p;
PSSM q;
p.getSC<SC<double> >().value = 5;
q.getSC<SC<double> >().value = 3;
std::cout << mul<double>(p,q);
}
I don't understand where the problem is. Can anyone understand how to work around it, or explain the nature of the problem in g++?
The problem is syntactic. You should use the template disambiguator in this case, so that your invocation of a member function template will be correctly parsed:
return (G.template getSC<SC<T> >().value * S.template getSC<SC<T> >().value);
// ^^^^^^^^^ ^^^^^^^^^
This disambiguator helps the compiler recognizing that what follows G. is a member template specialization and not, for instance, a data member called getSC followed by a < (less than).
The Standard reference for the template disambiguator is Paragraph 14.2/4 of the C++11 Standard:
When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.[ Example:
struct X {
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
T* p1 = p->alloc<200>(); // ill-formed: < means less than
T* p2 = p->template alloc<200>(); // OK: < starts template argument list
T::adjust<100>(); // ill-formed: < means less than
T::template adjust<100>(); // OK: < starts template argument list
}
—end example ]
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.
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).