piyo piyo - 3 months ago 9
C++ Question

C++ noexcept declaration changes template deduction

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

Answer

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);
}
Comments