The following code will not compile with G++ 4.5 or 4.6 (snapshot). It will compile with the Digital Mars Compiler 8.42n.
template <int I>
struct Foo {
template <int J>
void bar(int x) {}
};
template <int I>
void test()
{
Foo<I> a;
a.bar<8>(9);
};
int main(int argc, char *argv[]) {
test<0>();
return 0;
}
The error message is:
bugbody.cpp: In function 'void test() [with int I = 0]':
bugbody.cpp:16:11: instantiated from here
bugbody.cpp:11:3: error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'
Is the program valid C++?
Since the bar in a.bar is a dependent name, the compiler doesn’t know that it’s a template. You need to specify this, otherwise the compiler interprets the subsequent <…> as binary comparison operators:
a.template bar<8>(9);
The compiler behaves correctly.
The reason for this behaviour lies in specialisation. Imagine that you have specialised the Foo class for some value:
template <>
struct Foo<0> {
int bar;
};
Now your original code would compile, but it would mean something completely different. In the first parsing pass, the compiler doesn’t yet know which specialisation of Foo you’re using here so it needs to disambiguate between the two possible usages of a.bar; hence the keyword template to show the compiler that the subsequent <…> are template arguments.
Related
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.
class A { int a; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(T::a)> {};
int main() { test<A> a; }
The code above compiles without error on clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final), but fails to compile on g++-5 (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904 and g++-6 (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901 with errors like this:
main.cpp: In function ‘int main()’:
main.cpp:9:22: error: ‘int A::a’ is private within this context
int main() { test<A> a; }
^
main.cpp:1:15: note: declared private here
class A { int a; };
In both cases I compiled with -std=c++11, but the effect is the same for -std=c++14 and -std=c++1z.
Which compiler is correct here? I assumed that, at least since C++11, accessing private members during template substitution should trigger SFINAE, implying that clang is correct and gcc is not. Is there some additional rule I am unaware of?
For referencce, I am thinking of the note in §14.8.2/8 of the current standard draft N4606:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed, with a diagnostic required, if written using the
substituted arguments. [ Note: If no diagnostic is required, the
program is still ill-formed. Access checking is done as part of the
substitution process. — end note ]
Using a member function and a member function pointer instead is accepted by both compilers:
class A { void a() {}; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(&T::a)> {};
int main() { test<A> a; }
This is very interesting! I think it's a g++ compiler bug and I think that this is what happens. I've tried several alterations of your code with g++ 4.9.3 and clang 3.7.0 .
Although there are somewhat different rules to function vs class template instantiation, I believe that these are the general steps to a template instantiation:
Compiler reads source file with template definitions.
Name lookup (may trigger ADL) : It is the procedure by which a name,
when encountered in a program, is associated with the declaration
that introduced it.
(http://en.cppreference.com/w/cpp/language/lookup)
Template argument specification / deduction : In order to
instantiate a function template, every template argument must be
known, but not every template argument has to be specified. When
possible, the compiler will deduce the missing template arguments
from the function arguments.
(http://en.cppreference.com/w/cpp/language/template_argument_deduction)
Template substitution (may trigger SFINAE) : Every uses of a
template parameter in the function parameter list is replaced with
the corresponding template arguments.Substitution failure (that is,
failure to replace template parameters with the deduced or provided
template arguments) of a function template removes the function
template from the overload set. (http://en.cppreference.com/w/cpp/language/function_template#Template_argument_substitution)
Forming the overload set: Before overload resolution begins, the
functions selected by name lookup and template argument deduction
are combined to form the set of candidate functions.
(http://en.cppreference.com/w/cpp/language/overload_resolution#Details)
Overload resolution : In general, the candidate function whose
parameters match the arguments most closely is the one that is
called.
(http://en.cppreference.com/w/cpp/language/overload_resolution)
Template Instantiation : the template arguments must be determined
so that the compiler can generate an actual function (or class, from
a class template). (http://en.cppreference.com/w/cpp/language/function_template#Function_template_instantiation)
Compiler generates code.
I'll keep these bullet-points as guidelines and references for later on. Furthermore, I'll refer to template evaluation from steps 1-6. If you find anything wrong in the above list please feel free to change it or comment so that I can make the changes.
In the following example:
class A {};
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from both compilers:
Using None
This example compiles fine in both g++ and clang, because, when the compiler completes the evaluation process of all the templates, it will only choose to instantiate the first template for being the best match to the template arguments used to create the object in main(). Also, the template substitution process fails when the compiler fails to deduce T::a (SFINAE). Furthermore, due to the argument mismatch, the specialization will not be included in the overload set and will not be instantiated.
Should we add the second template argument, like this:
test<A , decltype(A::a)> a;
The code will not compile and both compilers would complain of:
error: no member named 'a' in 'A'
In the following example however, things start becoming weird:
class A { int a; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from clang:
Using None
Output from g++:
error: ‘int A::a’ is private
To begin with, I think that this would have been a nice warning. But why an error? The template won't even get instantiated. Considering the previous example, and the fact that pointers-to-members are constant values known at compile time, it seems, that when clang completes the template evaluation stage, with the SFINAE occuring at template substitution, it accurately instantiates the first template and ignores the specialization. But when g++ goes through the substitution process, and looks for the variable T::a, it sees that it is a private member, and instead of saying SFINAE, it prompts with the error above. I think that this is where the bug is, considering this bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61806
Now, the curious part is in the next example, which uses the private member function:
class A{ void a() {}; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T,decltype(&T::a)>
{ test(){std::cout<< "Using A::a" <<std::endl;} };
int main()
{ test<A> a; }
Output by both compilers:
Using None
If the previous explanation is true, then why doesn't g++ prompt an error when the private member function is used? Again, this is only an assumption based on the outputs, but I think that this bit actually works as it should. Long story short, SFINAE kicks in, the specialization is discarded from the overload set, and only the first template gets instantiated. Maybe there's more to it than meets the eye, but if we explicitly specify the second template argument, both compilers will prompt the same error.
int main()
{ test<A , decltype(&A::a)> b; }
Output by both compilers:
error: ‘void A::a()’ is private
Anyway, this is the final code I've been using. To demonstrate the outputs, I've made the class public. As an interesting event, I've added a nullptr to point to the member function directly. The type from decltype(((T*)nullptr)->f()) is void, and from the example below, a and c are both invoked by the specialization rather than the first template. The reason is because the second template is more specialized than the first and therefore is the best match for both of them (kill two birds with one stone) (Template Formal Ordering Rules : https://stackoverflow.com/a/9993549/2754510). The type from decltype(&T::f) is M4GolfFvvE (possible translation: Men 4 Golf Fear very vicious Elk), which thanks to boost::typeindex::type_id_with_cvr, it is demangled into void (Golf::*)().
#include <iostream>
#include <boost/type_index.hpp>
class Golf
{
public:
int v;
void f()
{};
};
template<typename T>
using var_t = decltype(T::v);
template<typename T>
using func_t = decltype(&T::f);
//using func_t = decltype(((T*)nullptr)->f()); //void
template<typename, typename = void>
struct test
{
test(){std::cout<< "Using None" <<std::endl;}
};
template<typename T>
struct test<T,var_t<T> >
{
test(){std::cout<< "Using Golf::v" <<std::endl;}
};
template<typename T>
struct test<T,func_t<T> >
{
test(){std::cout<< "Using Golf::f" <<std::endl;}
};
int main()
{
test<Golf> a;
test<Golf,var_t<Golf> > b;
test<Golf,func_t<Golf> > c;
using boost::typeindex::type_id_with_cvr;
std::cout<< typeid(func_t<Golf>).name() << " -> " << type_id_with_cvr<func_t<Golf>>().pretty_name() <<std::endl;
}
Output from both compilers (func_t = decltype(&T::f)):
Using None
Using Golf::v
Using Golf::f
M4GolfFvvE -> void (Golf::*)()
Output from both compilers (func_t = decltype(((T*)nullptr)->f())):
Using Golf::f
Using Golf::v
Using Golf::f
v -> void
Edit:
According to #Dr.Gut (comment below) this bug continues to exist in gcc 9.2. However I found a "hack" around this using std::declval which makes it look even weirder.
#include <utility>
class A
{
int a;
};
template<typename, typename = void>
class test
{};
template<typename T>
class test<T,decltype(std::declval<A>().a)>
{};
int main()
{
test<A> a;
}
Online example: https://rextester.com/BUFU29474
The code compiles and runs fine in g++ and vc++ but fails in clang++.
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>(); }
When trying to offer functions for const and non-const template arguments in my library I came across a strange problem. The following source code is a minimal example phenomenon:
#include <iostream>
template<typename some_type>
struct some_meta_class;
template<>
struct some_meta_class<int>
{
typedef void type;
};
template<typename some_type>
struct return_type
{
typedef typename some_meta_class< some_type >::type test;
typedef void type;
};
template<typename type>
typename return_type<type>::type foo( type & in )
{
std::cout << "non-const" << std::endl;
}
template<typename type>
void foo( type const & in )
{
std::cout << "const" << std::endl;
}
int main()
{
int i;
int const & ciref = i;
foo(ciref);
}
I tried to implement a non-const version and a const version for foo but unfortunately this code won't compile on CLANG 3.0 and gcc 4.6.3.
main.cpp:18:22: error: implicit instantiation of undefined template
'some_meta_class'
So for some reason the compiler wants to use the non-const version of foo for a const int-reference. This obviously leads to the error above because there is no implementation for some_meta_class. The strange thing is, that if you do one of the following changes, the code compile well and works:
uncomment/remove the non-const version
uncomemnt/remove the typedef of return_type::test
This example is of course minimalistic and pure academic. In my library I came across this problem because the const and non-const version return different types. I managed this problem by using a helper class which is partially specialized.
But why does the example above result in such strange behaviour? Why doesn't the compiler want to use the non-const version where the const version is valid and and matches better?
The reason is the way function call resolution is performed, together with template argument deduction and substitution.
Firstly, name lookup is performed. This gives you two functions with a matching name foo().
Secondly, type deduction is performed: for each of the template functions with a matching name, the compiler tries to deduce the function template arguments which would yield a viable match. The error you get happens in this phase.
Thirdly, overload resolution enters the game. This is only after type deduction has been performed and the signatures of the viable functions for resolving the call have been determined, which makes sense: the compiler can meaningfully resolve your function call only after it has found out the exact signature of all the candidates.
The fact that you get an error related to the non-const overload is not because the compiler chooses it as a most viable candidate for resolving the call (that would be step 3), but because the compiler produces an error while instantiating its return type to determine its signature, during step 2.
It is not entirely obvious why this results in an error though, because one might expect that SFINAE applies (Substitution Failure Is Not An Error). To clarify this, we might consider a simpler example:
template<typename T> struct X { };
template<typename T> typename X<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // Selects overload 2
}
In this example, SFINAE applies: during step 2, the compiler will deduce T for each of the two overloads above, and try to determine their signatures. In case of overload 1, this results in a substitution failure: X<const int> does not define any type (no typedef in X). However, due to SFINAE, the compiler simply discards it and finds that overload 2 is a viable match. Thus, it picks it.
Let's now change the example slightly in a way that mirrors your example:
template<typename T> struct X { };
template<typename Y>
struct R { typedef typename X<Y>::type type; };
// Notice the small change from X<T> into R<T>!
template<typename T> typename R<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // ERROR! Cannot instantiate R<int const>
}
What has changed is that overload 1 no longer returns X<T>::type, but rather R<T>::type. This is in turn the same as X<T>::type because of the typedef declaration in R, so one might expect it to yield the same result. However, in this case you get a compilation error. Why?
The Standard has the answer (Paragraph 14.8.3/8):
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
Clearly, the second example (as well as yours) generates an error in a nested context, so SFINAE does not apply. I believe this answers your question.
By the way, it is interesting to notice, that this has changed since C++03, which more generally stated (Paragraph 14.8.2/2):
[...] If a substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails. [...]
If you are curious about the reasons why things have changed, this paper might give you an idea.
I have never gotten a great explanation of how template argument deduction really works, so I'm not sure how to explain behavior I'm seeing in the following:
template<typename T>
struct Base
{
protected:
template<bool aBool = true>
static void Bar(int)
{
}
};
template<typename T>
class Derived : public Base<T>
{
public:
void Foo() { Base<T>::Bar<false>(5); }
};
int main()
{
Derived<int> v;
v.Foo();
return 0;
}
This code won't build, and gives the error:
main.cpp: In instantiation of 'void Derived<T>::Foo() [with T = int]':
main.cpp:25:8: required from here main.cpp:19:15: error: invalid
operands of types '<unresolved overloaded function type>' and 'bool'
to binary 'operator<'
If you change the 2 Base<T>s in Derived to Base<int>, it compiles. If you change the call to Bar() to Base<T>::template Bar<false>(5);, it also compiles.
The one-liner I saw as an explanation for this is that the compiler doesn't know that Bar is a template, presumably because it doesn't know what Base is until a specialization of Derived is declared. But once the compiler starts generating code for Foo(), Base<T> has already been defined, and the type of Bar can be determined. What is causing the compiler to assume the symbol Bar is not a template, and attempting to apply operator<() instead?
I assume it has to do with the rules of when templates are evaluated in the compilation process - I guess what I'm looking for is a good comprehensive explanation of this process, such that the next time I run into code like the below, I can deduce the answer without the help of the good people on stack overflow.
Note I'm compiling with g++ 4.7, with c++x11 support.
void Foo() { Base<T>::Bar<false>(5); }
In this context Base<T> is a dependent name. To access a member template of a dependent name you need to add the template keyword:
void Foo() { Base<T>::template Bar<false>(5); }
Otherwise Base<T>::Bar will be parsed as a non-template member and < as less-than.
As of why the template is required, the reason is two-phase lookup. The error is triggered during the first pass, before the type is substituted, so the compiler does not know what is the definition of Base<T>. Consider for example that you added an specialization of Bar for int that had a non-template Bar member (say for example an int member). Before substituting T into Foo, the compiler does not know if there is an specialization for the type.