c++ noexcept声明改变了模板演绎

C++ noexcept declaration changes template deduction

本文关键字:演绎 改变 noexcept 声明 c++      更新时间:2023-10-16

我正在修补确认Effective Modern c++第91页上的例子,然后我遇到了一个似乎很奇怪的问题。这段代码

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

给我一个类似

的错误

错误:请求成员' front '在' a ',它是非类类型' int ' void doStuff(C&一个,C&b) noexcept (noexcept (doStuff (a.front (),b.front ()))) {

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

我做错了什么?

谢谢

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

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&)替换专门化

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