【问题标题】:C++ noexcept declaration changes template deductionC++ noexcept 声明更改模板推导
【发布时间】:2016-12-25 18:26:43
【问题描述】:

我正在努力确认 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);
}

给我一​​个类似的错误

error: 在‘a’中请求成员‘front’,它是非类类型的 ‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {

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

这是 gcc 5.4 的版本。

我做错了什么?

谢谢

【问题讨论】:

  • 请记住,模板是 compile.time 功能。在定义doStuff 函数的第一个版本时,编译器对int 的特化一无所知。
  • 还有doStuffnoexcept规范中首次使用时没有声明的问题。

标签: c++ templates noexcept


【解决方案1】:

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

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

【讨论】:

  • int 重载真的必须是第一个吗? noexcept 说明符中的 doStuff 不是从属符号,这意味着它直到实例化才被解析?
  • 也许令人惊讶的是,C=std::vector&lt;std::vector&lt;int&gt;&gt; 会失败。
  • 哇,这是怎么回事? adl 不是从属名称,是吗?
  • 好的。这样可行。我自然地认为这与尚未声明的专业化有关,但我知道必须有一种正确的方法来处理这些情况,因为我在标准库中使用某些东西。
  • 嗯。这有效,但我不确定如何。我假设使用命名空间至少部分负责导致所需的延迟,但它仍然可以在没有额外命名空间的情况下工作。
【解决方案2】:

模板特化不是重载。您对doStuff&lt;int&gt; 的专业化不是doStuff&lt;C&gt; 的重载,它是一种专业化。所以重载决议不考虑它,模板实例化会考虑它,如果原始被重载决议选择。将您的专业化替换为重载(非模板,占用两个 int&amp;s)

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

【讨论】:

    猜你喜欢
    • 2019-01-01
    • 2015-06-28
    • 1970-01-01
    • 2015-10-17
    • 2011-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    相关资源
    最近更新 更多