Reputation: 471
I was tinkering to confirm the example on page 91 of Effective Modern C++, and I ran into what seems to be a strange issue. This code
template<typename C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
std::cout << "container version" << std::endl;
}
template<>
void doStuff<int>(int& x, int& y) noexcept {
std::cout << "int version" << std::endl;
}
int main() {
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5, 6};
int x = 5;
int y = 6;
doStuff(x, y);
doStuff(v1, v2);
}
Gives me an error like
error: request for member ‘front’ in ‘a’, which is of non-class type ‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
So, it seems like the top version of doStuff is being called, even though a.front() and b.front() should be returning references to ints. If I remove all the noexcept declarations from the code, I get the expected output.
This is with gcc 5.4.
What am I doing wrong?
Thanks
Upvotes: 8
Views: 846
Reputation: 303387
The problem is, when the name lookup at this point:
template<typename C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
// ^^^^^^^
will just find one doStuff()
: your function template. The specialization hasn't been declared yet, so it isn't considered.
First thing to do is to simply avoid specializations. They're awkward. But then the real fix would be to stick in an extra empty type solely for argument-dependent lookup purposes. This will add a dependent name to the noexcept
lookup that will delay invocation until instantiation:
namespace N {
struct adl { };
void doStuff(adl, int& , int& ) noexcept {
std::cout << "int version" << std::endl;
}
template<typename C>
void doStuff(adl, C& a, C& b) noexcept(noexcept(doStuff(adl{}, a.front(), b.front()))) {
std::cout << "container version" << std::endl;
}
}
template <class C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(N::adl{}, a, b)))
{
doStuff(N::adl{}, a, b);
}
Upvotes: 7
Reputation: 7339
Template specializations are not overloads. Your specialization for doStuff<int>
is not an overload of doStuff<C>
, it is a specialization. So overload resolution doesn't consider it, template instantiation will consider it, if the original is selected by overload resolution. Replace your specialization with an overload (non-template, taking two int&
s)
void doStuff(int& a, int& b) noexcept;
Upvotes: 2