在模板类型推导之前评估 noexcept 说明符
Evaluating noexcept specifier before template type deduction
请看下面代码:
#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 不是适当的重载(由于 tag
和 int
之间的不兼容)之前尝试计算 noexcept 说明符,因此应该被丢弃。因此,它尝试评估 is_noexcept<int>::value
并发现 noexcept(A{std::declval<int>()})
格式错误。由于这不是在直接上下文中发生的,因此这不是 SFINAE 的用武之地,所以出现严重错误。
(事实上,我对此不太确定,但我已经确认,如果我在 /*Here*/
处放置 noexcept(A{std::declval<Args>()...})
而不是 is_noexcept<Args...>::value
来使内部失败在直接上下文中,MSVC 编译器愉快地丢弃 #1 并调用 #2。这样对吗?)
我怀疑GCC/Clang是对的,MSVC是错的,到底哪个是对的?
这是一个 MSVC 错误。 CWG 1330, in [except.spec]/13 的结果是:
An exception specification is considered to be needed when:
- in an expression, the function is the unique lookup result or the selected member of a set of overloaded functions ([basic.lookup], [over.match], [over.over]);
- the function is odr-used or, if it appears in an unevaluated operand, would be odr-used if the expression were potentially-evaluated;
- the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function);
- the function is defined; or
- the exception specification is needed for a defaulted special member function that calls the function. [ Note: A defaulted declaration does not require the exception specification of a base member function to be evaluated until the implicit exception specification of the derived function is needed, but an explicit noexcept-specifier needs the implicit exception specification to compare against. — end note ]
The exception specification of a defaulted special member function is evaluated as described above only when needed; similarly, the noexcept-specifier of a specialization of a function template or member function of a class template is instantiated only when needed.
不需要 B(tag, Args&&...)
的异常规范(我们不满足任何这些项目符号),因此不应实例化它。
另请注意,异常规范中的替换失败 不是 直接上下文的一部分,并且不会是 SFINAE 友好错误。
请看下面代码:
#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 不是适当的重载(由于 tag
和 int
之间的不兼容)之前尝试计算 noexcept 说明符,因此应该被丢弃。因此,它尝试评估 is_noexcept<int>::value
并发现 noexcept(A{std::declval<int>()})
格式错误。由于这不是在直接上下文中发生的,因此这不是 SFINAE 的用武之地,所以出现严重错误。
(事实上,我对此不太确定,但我已经确认,如果我在 /*Here*/
处放置 noexcept(A{std::declval<Args>()...})
而不是 is_noexcept<Args...>::value
来使内部失败在直接上下文中,MSVC 编译器愉快地丢弃 #1 并调用 #2。这样对吗?)
我怀疑GCC/Clang是对的,MSVC是错的,到底哪个是对的?
这是一个 MSVC 错误。 CWG 1330, in [except.spec]/13 的结果是:
An exception specification is considered to be needed when:
- in an expression, the function is the unique lookup result or the selected member of a set of overloaded functions ([basic.lookup], [over.match], [over.over]);
- the function is odr-used or, if it appears in an unevaluated operand, would be odr-used if the expression were potentially-evaluated;
- the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function);
- the function is defined; or
- the exception specification is needed for a defaulted special member function that calls the function. [ Note: A defaulted declaration does not require the exception specification of a base member function to be evaluated until the implicit exception specification of the derived function is needed, but an explicit noexcept-specifier needs the implicit exception specification to compare against. — end note ]
The exception specification of a defaulted special member function is evaluated as described above only when needed; similarly, the noexcept-specifier of a specialization of a function template or member function of a class template is instantiated only when needed.
不需要 B(tag, Args&&...)
的异常规范(我们不满足任何这些项目符号),因此不应实例化它。
另请注意,异常规范中的替换失败 不是 直接上下文的一部分,并且不会是 SFINAE 友好错误。