noexcept 表达式与类型特征

8

我将学习如何使用条件noexcept,并遇到了这个问题。假设我有一个类:

template<typename T>
class Wrapper {
public:
    Wrapper(T&& value) noexcept(/* ??? */)
        : value_(std::move(value))
    {}

private:
    T value_;
};

对于/* ??? */ 部分,我认为我们可以使用noexcept(T(std::move(value)))std::is_nothrow_move_constructible<T>::value中的任何一个,直到我偶然发现了这个
所以如果我使用noexcept(noexcept(T(std::move(value)))),严格来说,我是在说“当构造和销毁一个Tnoexcept时,此构造函数才是noexcept”?
尽管抛出异常的析构函数应该被点燃并烧掉。

3
你能否使用例如 noexcept(new T(std::move(value))) 或类似的方式解决这个问题?由于它是一个未求值表达式,实际上并没有分配任何东西,但也应该特别地“泄漏”,所以析构函数不应该参与其中...... 我猜你可能需要使用 no-throw 版本的 new,因为你不想检测到 std::bad_alloc - Chris Beck
1个回答

6

很好的问题,另请参阅此语言缺陷讨论。从名称上看,std::is_nothrow_move_constructible<T>::value 显然只应与从rvalue构造有关(但实际上也可能与销毁有关),而noexcept(T(std::move(value)))总是与构造和销毁都有关。

因此,在您的情况下,最保险的方法是使用放置new (placement new),避免了std::is_nothrow_move_constructible特性的未解决问题,并避免了Chris Beck评论中提到的std::bad_alloc问题。同样地,对于包装器(wrapper)的析构函数,使用T的析构函数。

template<typename T>
class Wrapper {
public:
    Wrapper(T&& value) noexcept(new(nullptr) T(std::move(value)))
        : value_(std::move(value))
    {}
    ~Wrapper() noexcept(noexcept(value_.T::~T()))
    {}
private:
    T value_;
};

2
“很明显std::is_nothrow_move_constructible<T>::value只应该与构造函数有关”——这一点并不清楚,问题仍未解决,而且肯定没有明确的共识认为提交者的期望是正确的,或者标准中存在任何缺陷。 - Jonathan Wakely
@JonathanWakely 我理解这一点,但对此提出异议:它不清楚且令人困惑。因此应该有一个MoveConstructibleAndDescructible的概念以及类似的特征。在某些情况下,人们可能希望区分这些事情。 - Walter
在我看来,traits 应该做它们所说的那样,不多不少。其他任何事情都会导致混乱、错误的代码和灾难。 - Walter
1
@T.C. 是的,这就是为什么我说 is_move_constructible 与 MoveConstructible 有关(但并非完全相同)。它仍然比“具有移动构造函数”更接近 MoveConstructible,因为有许多类型是 MoveConstructible,并且对于这些类型,is_move_constructible 为 true,但它们并没有移动构造函数。 - Jonathan Wakely
1
@Walter,有时候你想要区分它们,但这种情况很少见(抛出析构函数很少见,而不可析构类型更是罕见)。概念和特性对应于标准库中所需的属性(这些属性在用户代码中也更常见)。如果您想要不同的特性用于不同的情况,请编写一个。std::is_move_constructible执行其标准规定的功能,我认为这不是一个缺陷。 - Jonathan Wakely
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接