【问题标题】:Is noexcept deduction allowed in class template partial specialization?类模板偏特化中是否允许进行 noexcept 扣除?
【发布时间】:2017-05-29 16:43:54
【问题描述】:

对于下面的程序,Clang 5 (trunk) 报告 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 相关。

【问题讨论】:

  • 如果编译器在编译时出现段错误,这绝不是一个好兆头。您要提交错误报告吗?
  • 一个已经存在here
  • 您是否只想使用类型特征来检测函数是否为noexcept
  • 为什么不像this那样干脆干脆呢?
  • 这是一个(损坏的)GCC 扩展。

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


【解决方案1】:
  1. [temp.deduct.type]/8 列出了可以从中推断出模板参数的所有类型的形式。 exception-specification 不在列表中,因此不可推断。
  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) 的值具有以下 sn-p:

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

【讨论】:

  • IIRC,Clang 将推论实现为后备推论:仅当无法以其他方式推导 E 时,才会使用异常规范进行推导。你能确认一下吗?
  • @JohannesSchaub-litb 它已实现two days ago...我假设您指的是“从数组绑定推断”处理?这不会影响我的示例。
  • 另外,这种处理只影响类型,而不影响值。
  • 这是CWG2355 虽然我以为它被采纳了但现在我找不到证据。
【解决方案2】:

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

N4618 §8.3.5/1 [dcl.fct] 状态(强调我的

在声明 T D 中,D 的形式为
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifier选择属性说明符序列选择

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

所以这意味着函数的类型要么包含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] 讨论了这个 wrt 复合指针类型

谢天谢地,这是非法的:

void (*fp)() noexcept(true) = &foo;

(请参阅 N4618 §4.13 [conv.fctptr] 中的示例以供参考)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-15
    • 2012-02-13
    • 1970-01-01
    • 2013-09-05
    • 2019-03-29
    • 2011-02-02
    • 1970-01-01
    相关资源
    最近更新 更多