【问题标题】:SFINAE and noexcept specifierSFINAE 和 noexcept 说明符
【发布时间】:2016-02-13 09:49:29
【问题描述】:

在函数模板的重载解析期间,noexcept 说明符括号中的表达式是否参与 SFINAE?

我想为聚合创建一个包装器,并希望 std::is_constructible 谓词能够正常工作:

template< typename type >
struct embrace
    : type
{

    template< typename ...arguments >
    embrace(arguments &&... _arguments) noexcept(noexcept(type{std::forward< arguments >(_arguments)...}))
        : type{std::forward< arguments >(_arguments)...} // braces 
    { ; }

};

int
main()
{
    struct S { int i; double j; }; // aggregate
    using E = embrace< S >;
    E b(1, 1.0); // "parentheses"-constructible => can be used as usual types
    b.i = 1; b.j = 2.0; // accessible
    static_assert(std::is_constructible< E, int, double >{});
    static_assert(std::is_constructible< E, struct B >{}); // want hard error here
    return EXIT_SUCCESS;
}

但我尝试在noexcept 规范中使用noexcept 运算符来启用SFINAE 失败了,模板化构造函数接受传递给它的所有内容。如何限制构造函数?

标准不允许专门化来自&lt;type_traits&gt; 的任何谓词。通常如何处理接受可变参数模板参数包和 SFINAE 的 c-tors?是否存在僵局和固有的语言缺陷?

【问题讨论】:

  • 这是什么编译器?
  • @ThomasMcLeod clang 3.7
  • @ThomasMcLeod g++ 给出了早期的硬错误(在 c-tor 本身而不是 SFINAE 中)。

标签: c++ c++11 constructor c++14 sfinae


【解决方案1】:

SFINAE 根本不适用于异常规范,无论noexcept 是否是函数类型的一部分。

参见 [temp.deduct]/7 中的注释:

替换发生在所有类型和表达式中 函数类型和模板参数声明。这 表达式不仅包括常量表达式,例如 出现在数组边界或作为非类型模板参数,但也 sizeof 中的一般表达式(即非常量表达式), decltype 和其他允许非常量表达式的上下文。这 替换按词汇顺序进行,并在满足条件时停止 遇到导致扣减失败。 [ 注意:等价 异常规范中的替换仅在 exception-specification 被实例化,此时如果替换导致无效类型或 表达。 —尾注 ]

P0012R1 didn't change anything 在这方面。

Piotr 的回答涵盖了您的代码的修复。

【讨论】:

  • 你是不是想说C++17不会在这方面带来新的东西?
  • @Orient P0012R1 是“向类型系统添加异常规范”论文,它并没有改变 SFINAE 关于异常规范的任何内容。距离我们还有两年的时间,现在说 C++17 不会做 X 还为时过早。
【解决方案2】:

如何限制构造函数?

#include <utility>

template <typename type>
struct embrace : type
{
    template <typename... arguments
            , typename = decltype(type{std::declval<arguments>()...})>
    embrace(arguments&&... _arguments)
        noexcept(noexcept(type{std::forward<arguments>(_arguments)...}))
        : type{std::forward<arguments>(_arguments)...}
    {
    }
};

DEMO

(或更短):

#include <utility>

template <typename type>
struct embrace : type
{
    template <typename... arguments
            , bool NoExcept = noexcept(type{std::declval<arguments>()...})>
    constexpr
    embrace(arguments&&... _arguments)
        noexcept(NoExcept)
        : type{std::forward<arguments>(_arguments)...}
    {
    }
};

DEMO 2

【讨论】:

  • 我认为不允许使用非尾随模板参数包。你能阐明这一点吗?
  • 无论哪种方式都是完美的解决方案。
  • @Orient 可以推导就可以。参数包必须仅用于主类模板和别名模板的末尾
  • 问题:对于演示 2,我不熟悉将 constexpr 放在模板参数之后。它有什么作用,为什么需要它?
【解决方案3】:

noexcept 说明符括号中的表达式是否参与 函数模板重载解析期间的 SFINAE?

它不参与模板推导,因为noexcept 说明符不是函数类型的一部分。

noexcept 规范不是函数类型的一部分。 (直到 C++17)

Source

因此,当您的模板参数type 被推导时,noexcept 不是推导类型的一部分。您的编译器似乎为任何类型返回 true,这就是为什么您无法检测它是否为 noexcept 的原因;这就是为什么一切都被接受的原因。

我遇到了同样的问题。你可以在这里查看我的问题/答案:

How can I detect whether a template argument is a noexcept function?

基本上,您唯一的选择是等待符合 C++17 的编译器。

【讨论】:

  • 似乎-std=gnu++1z 目前不允许使用答案中提到的内容。在clang++ 中也不在g++ 中。
  • 为什么g++clang++ 的编译器行为不同?
  • @Orient 这取决于它们与 C++17 的兼容程度,否则您将无法从推导的类型中获得 noexcept 部分。
  • 这是一个截然不同的问题。
猜你喜欢
  • 2013-03-05
  • 2023-03-20
  • 2020-05-08
  • 2015-06-28
  • 2014-01-17
  • 2019-03-11
  • 2021-07-05
  • 2021-12-21
  • 1970-01-01
相关资源
最近更新 更多