C++ noexcept 声明更改模板推导

C++ noexcept declaration changes template deduction

我正在修补以确认 Effective Modern C++ 第 91 页上的示例,然后我 运行 陷入了一个看似 st运行ge 的问题。此代码

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

给我一个像

这样的错误

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()))) {

因此,似乎正在调用 doStuff 的顶级版本,即使 a.front() 和 b.front() 应该返回对 int 的引用。如果我从代码中删除所有 noexcept 声明,我会得到预期的输出。

这是 gcc 5.4。

我做错了什么?

谢谢

问题是,此时查找名称时:

template<typename C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
//                                         ^^^^^^^

只会找到一个 doStuff(): 您的函数模板。专业化尚未宣布,因此不考虑。

首先要做的就是避免专业化。他们很尴尬。但是真正的解决方法是为了依赖于参数的查找目的而坚持一个额外的空类型。这将向 noexcept 查找添加一个从属名称,该名称将延迟调用直到实例化:

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

模板特化不是重载。 doStuff<int> 的专业化不是 doStuff<C> 的重载,而是专业化。所以重载决议不考虑它,模板实例化会考虑它,如果原来是通过重载决议选择的。用重载替换你的专业化(非模板,需要两个 int&s)

void doStuff(int& a, int& b) noexcept;