类模板部分特化是否允许noexcept演绎?

时间:2017-05-29 16:43:55

标签: c++ language-lawyer c++17 typetraits

对于下面的程序,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有关。

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

据我所知,noexcept说明符中包含的表达式的值不会成为函数类型的一部分。*

N4618§8.3.5/ 1 [dcl.fct]陈述(强调我的

  

在声明T DD的形式为<   D1 parameter-declaration-clause cv-qualifier-seq opt ref-qualifier opt noexcept-specifier < sub> opt attribute-specifier-seq opt

   并且声明T D1中包含的 declarator-id 的类型是“ derived-declarator-type-list T”,类型D中的 declarator-id 是“ derived-declarator-type-list noexcept opt 函数(< em> parameter-declaration-clause ) cv-qualifier-seq opt ref-qualifier opt 返回T“ ,当且仅当异常规范(15.4)非抛出时,存在可选的 noexcept。可选的 attribute-specifier-seq 属于函数类型。

所以这意味着函数的类型包括noexcept或不包括noexcept(expr);如果false中的表达式 expr 评估为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被声明为void (*fp)() noexcept(true) = &foo; ,我们也可以为它分配函数指针!所以它有误导性; 我找不到它的标准参考,但似乎规则允许这样的转换隐式地以适应大量的向后兼容性。§5/ 14.2 [expr]谈论这个wrt到复合指针类型

幸运的是,这是非法的:

Computer Name: $env:computername 

(参见N4618§4.13[conv.fctptr]中的例子以供参考)。

*在N4320中提出了将异常说明符集成到类型系统中(已被C ++ 17标准采用)