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 noexceptopt 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 标准采纳)

  1. [temp.deduct.type]/8 列出了可以从中推导出模板参数的所有形式的类型。 异常规范 不在列表中,因此不可推断。
  2. an extension 一样,GCC 允许从 noexcept 推导以简化 std::is_function 的实施。看起来扩展只经过了非常轻微的测试。
  3. 该扩展最初是由 Clang 的维护者建议的,并且似乎在委员会中得到了一些支持,但是 it's not clear if it will eventually make its way into the standard.
  4. 这不是一个符合规范的扩展,因为它改变了定义明确的代码的含义,例如,g(f) 的值具有以下代码段:

    void f() noexcept;
    
    template<bool E = false, class R>
    constexpr bool g(R (*)() noexcept(E)){
        return E;
    }