【问题标题】:Evaluating noexcept specifier before template type deduction在模板类型推导之前评估 noexcept 说明符
【发布时间】:2019-01-01 05:25:03
【问题描述】:

请看以下代码:

#include <utility>

struct A {
  A(int, int) {}
};

struct tag {};

template <class... Args>
struct is_noexcept {
  static constexpr bool value = noexcept(A{std::declval<Args>()...});
};

struct B : A {
  //#1
  template <class... Args>
  B(tag, Args&&... args) noexcept(/*Here*/is_noexcept<Args...>::value) :
    A{std::forward<Args>(args)...} {}

  //#2
  B(int x, int y) : A{x, y} {}
};

int main()
{
  B x{0, 0};
}

此代码似乎被 GCC/Clang 接受,但 MSVC 2017 拒绝它。 似乎 MSVC 编译器在理解 #1 不是适当的重载(由于 tagint 之间的不兼容)之前尝试计算 noexcept 说明符,因此应该丢弃。因此,它尝试评估 is_noexcept&lt;int&gt;::value 并发现 noexcept(A{std::declval&lt;int&gt;()}) 格式错误。由于这不是在直接上下文中发生的,所以这不是 SFINAE 出现的地方,所以很难出错。

(事实上,我不太确定这一点,但我已经确认,如果我将 noexcept(A{std::declval&lt;Args&gt;()...}) 而不是 is_noexcept&lt;Args...&gt;::value 放在 /*Here*/ 以在直接上下文中发生故障,MSVC 编译器会很高兴丢弃 #1 并调用 #2。对吗?)

我怀疑 GCC/Clang 是对的,MSVC 是错的,但哪一个是对的?

【问题讨论】:

  • 我会说“像往常一样”。

标签: c++ c++11 language-lawyer noexcept


【解决方案1】:

这是一个 MSVC 错误。由于CWG 1330[except.spec]/13 中的规则是:

异常规范被认为是需要,当:

  • 在表达式中,函数是唯一的查找结果或一组重载函数([basic.lookup]、[over.match]、[over.over])的选定成员;
  • 函数是 odr-used,或者,如果它出现在未计算的操作数中,如果表达式被潜在地计算,它将是 odr-used;
  • 将异常规范与另一个声明的规范进行比较(例如,显式特化或重写虚函数);
  • 函数已定义;或
  • 调用该函数的默认特殊成员函数需要异常规范。 [ 注意: 在需要派生函数的隐式异常规范之前,默认声明不需要评估基成员函数的异常规范,但是显式的 noexcept 说明符需要隐式异常规范比较。 — 尾注 ]

默认的特殊成员函数的异常规范仅在需要时按上述方式进行评估;类似地,函数模板或类模板成员函数的特化的noexcept-specifier仅在需要时才被实例化。

不需要B(tag, Args&amp;&amp;...) 的异常规范(我们不满足任何这些项目符号),因此不应实例化它。

另请注意,异常规范中的替换失败不是直接上下文的一部分,并且不是 SFINAE 友好的错误。

【讨论】:

  • 谢谢!第一个项目符号和最后一句话似乎是在实际选择调用 B(tag, Arg&amp;&amp;...) 时评估 noexcept 说明符。正确的?我对第四个子弹有点困惑。函数模板或类模板的成员函数的“定义”是什么意思?还是第四个项目符号仅适用于非模板函数?
  • @JunekeyJeon 模板在实例化时定义。
  • 谢谢。而且,尽管我同意使用 SFINAE 使用 noexcept 规范通常不是一个好主意,即使这是可能的,你能解释一下为什么 noexcept 不是直接上下文的一部分吗?如果我的理解是正确的,从 C++17 开始,noexcept 现在是函数类型的一部分,任何“函数类型中使用的格式错误的表达式”都可能导致 SFINAE。不是吗?
  • @JunekeyJeon 这不是因为规则说它不是 - 您只有在选择了函数之后才评估异常说明符。那时我们已经完成了重载解决方案,来不及备份。
  • 啊,确实!谢谢:)
猜你喜欢
  • 2016-12-25
  • 2022-10-20
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 2016-02-13
  • 1970-01-01
相关资源
最近更新 更多