class 模板偏特化中是否允许扣除 noexcept?
Is noexcept deduction allowed in class template partial specialization?
对于下面的程序,Clang 5(主干)报告说 IsNoexcept
不可推导,而 GCC 7.1 会出现段错误。 标准(草案)对此有何规定?这是编译器 QOI 问题吗?
static_assert(__cpp_noexcept_function_type, "requires c++1z");
template<typename T>
struct is_noexcept;
template<bool IsNoexcept>
struct is_noexcept<void() noexcept(IsNoexcept)> {
static constexpr auto value = IsNoexcept;
};
static_assert(is_noexcept<void() noexcept>::value);
static_assert(!is_noexcept<void()>::value);
int main() {}
与提案 P0012 相关。
据我所知,noexcept
说明符中包含的表达式的值不会成为函数类型的一部分。*
N4618 §8.3.5/1 [dcl.fct] 状态(强调我的)
In a declaration T D
where D
has the form
D1
( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieroptattribute-specifier-seqopt
and the type of the contained declarator-id in the declaration T D1
is “derived-declarator-type-list T
”, the type of the declarator-id in D
is “derived-declarator-type-list noexcept
opt function of (parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returning T
”, where the optional noexcept
is present if and only if the exception specification (15.4) is non-throwing. The optional attribute-specifier-seq appertains to the function type.
所以这意味着函数的类型要么包含noexcept
,要么不包含;如果 noexcept(expr)
中的表达式 expr 的计算结果为 false
,则函数的类型将完全排除关键字 noexcept
。
所以你被迫做这样的事情:
template<typename T>
struct is_noexcept
{
static constexpr bool value = false;
};
template<>
struct is_noexcept<void() noexcept> {
static constexpr auto value = true;
};
但是,我认为这样的代码编译的事实具有误导性:
void (*fp)() noexcept(false);
因为以下函数的类型:
void foo() noexcept(false)
{
}
是void()
.
以及以下函数的类型:
void bar() noexcept(true)
{
}
是void() noexcept
但是,我们可以这样做:
void (*fp)() noexcept(false) = &bar;
fp();
即使 bar
声明为 noexcept
,我们也可以将函数指针分配给它!所以这是误导; 我找不到它的标准参考,但似乎规则隐式允许此类转换以适应令人难以置信的向后兼容性。 §5/14.2 [expr] 会谈关于 复合指针类型 。
幸运的是,这是非法的:
void (*fp)() noexcept(true) = &foo;
(请参阅 N4618 §4.13 [conv.fctptr] 中的示例以供参考)。
*在 N4320 中提出了将异常说明符集成到类型系统中(已被 C++17 标准采纳)
- [temp.deduct.type]/8 列出了可以从中推导出模板参数的所有形式的类型。 异常规范 不在列表中,因此不可推断。
- 与 an extension 一样,GCC 允许从
noexcept
推导以简化 std::is_function
的实施。看起来扩展只经过了非常轻微的测试。
- 该扩展最初是由 Clang 的维护者建议的,并且似乎在委员会中得到了一些支持,但是 it's not clear if it will eventually make its way into the standard.
这不是一个符合规范的扩展,因为它改变了定义明确的代码的含义,例如,g(f)
的值具有以下代码段:
void f() noexcept;
template<bool E = false, class R>
constexpr bool g(R (*)() noexcept(E)){
return E;
}
对于下面的程序,Clang 5(主干)报告说 IsNoexcept
不可推导,而 GCC 7.1 会出现段错误。 标准(草案)对此有何规定?这是编译器 QOI 问题吗?
static_assert(__cpp_noexcept_function_type, "requires c++1z");
template<typename T>
struct is_noexcept;
template<bool IsNoexcept>
struct is_noexcept<void() noexcept(IsNoexcept)> {
static constexpr auto value = IsNoexcept;
};
static_assert(is_noexcept<void() noexcept>::value);
static_assert(!is_noexcept<void()>::value);
int main() {}
与提案 P0012 相关。
据我所知,noexcept
说明符中包含的表达式的值不会成为函数类型的一部分。*
N4618 §8.3.5/1 [dcl.fct] 状态(强调我的)
In a declaration
T D
whereD
has the form
D1
( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieroptattribute-specifier-seqopt
and the type of the contained declarator-id in the declarationT D1
is “derived-declarator-type-listT
”, the type of the declarator-id inD
is “derived-declarator-type-listnoexcept
opt function of (parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returningT
”, where the optionalnoexcept
is present if and only if the exception specification (15.4) is non-throwing. The optional attribute-specifier-seq appertains to the function type.
所以这意味着函数的类型要么包含noexcept
,要么不包含;如果 noexcept(expr)
中的表达式 expr 的计算结果为 false
,则函数的类型将完全排除关键字 noexcept
。
所以你被迫做这样的事情:
template<typename T>
struct is_noexcept
{
static constexpr bool value = false;
};
template<>
struct is_noexcept<void() noexcept> {
static constexpr auto value = true;
};
但是,我认为这样的代码编译的事实具有误导性:
void (*fp)() noexcept(false);
因为以下函数的类型:
void foo() noexcept(false)
{
}
是void()
.
以及以下函数的类型:
void bar() noexcept(true)
{
}
是void() noexcept
但是,我们可以这样做:
void (*fp)() noexcept(false) = &bar;
fp();
即使 bar
声明为 noexcept
,我们也可以将函数指针分配给它!所以这是误导; 我找不到它的标准参考,但似乎规则隐式允许此类转换以适应令人难以置信的向后兼容性。 §5/14.2 [expr] 会谈关于 复合指针类型 。
幸运的是,这是非法的:
void (*fp)() noexcept(true) = &foo;
(请参阅 N4618 §4.13 [conv.fctptr] 中的示例以供参考)。
*在 N4320 中提出了将异常说明符集成到类型系统中(已被 C++17 标准采纳)
- [temp.deduct.type]/8 列出了可以从中推导出模板参数的所有形式的类型。 异常规范 不在列表中,因此不可推断。
- 与 an extension 一样,GCC 允许从
noexcept
推导以简化std::is_function
的实施。看起来扩展只经过了非常轻微的测试。 - 该扩展最初是由 Clang 的维护者建议的,并且似乎在委员会中得到了一些支持,但是 it's not clear if it will eventually make its way into the standard.
这不是一个符合规范的扩展,因为它改变了定义明确的代码的含义,例如,
g(f)
的值具有以下代码段:void f() noexcept; template<bool E = false, class R> constexpr bool g(R (*)() noexcept(E)){ return E; }