【问题标题】:How to properly specify noexcept with a C++ forwarding reference?如何使用 C++ 转发引用正确指定 noexcept?
【发布时间】:2020-11-18 16:45:38
【问题描述】:

举个例子:

#include <iostream>
#include <type_traits>
#include <utility>


struct Bar
{
    Bar() = default;
    Bar(Bar const&) noexcept(false) = default;
    Bar(Bar&&) noexcept(true) = default;
};

struct Baz
{
    Baz() = default;
    Baz(Baz const&) noexcept(true) = default;
    Baz(Baz&&) noexcept(false) = default;
};

template<typename T>
class Foo
{
    template<typename U, typename V>
    using enable_if_same = std::enable_if<std::is_same<typename std::remove_reference<U>::type, V>::value, U>;

public:
    template<typename U>
    Foo(typename enable_if_same<U, T>::type&& val) // noexcept( ? )
        : _val(std::forward<U>(val))
    {
    }

protected:
    T _val;
};


int main(void)
{
    std::cout << "Is the Bar copy constructor noexcept? " << std::is_nothrow_copy_constructible<Bar>::value << "\n";
    std::cout << "Is the Bar move constructor noexcept? " << std::is_nothrow_move_constructible<Bar>::value << "\n";
    std::cout << "Is the Foo<Bar> copy constructor noexcept? " << std::is_nothrow_copy_constructible<Foo<Bar>>::value << "\n";
    std::cout << "Is the Foo<Bar> move constructor noexcept? " << std::is_nothrow_move_constructible<Foo<Bar>>::value << "\n";

    std::cout << "\n";

    std::cout << "Is the Baz copy constructor noexcept? " << std::is_nothrow_copy_constructible<Baz>::value << "\n";
    std::cout << "Is the Baz move constructor noexcept? " << std::is_nothrow_move_constructible<Baz>::value << "\n";
    std::cout << "Is the Foo<Baz> copy constructor noexcept? " << std::is_nothrow_copy_constructible<Foo<Baz>>::value << "\n";
    std::cout << "Is the Foo<Baz> move constructor noexcept? " << std::is_nothrow_move_constructible<Foo<Baz>>::value << "\n";

    return 0;
}

编译并运行上述代码会产生预期的输出:

Is the Bar copy constructor noexcept? 0
Is the Bar move constructor noexcept? 1
Is the Foo<Bar> copy constructor noexcept? 0
Is the Foo<Bar> move constructor noexcept? 1

Is the Baz copy constructor noexcept? 1
Is the Baz move constructor noexcept? 0
Is the Foo<Baz> copy constructor noexcept? 1
Is the Foo<Baz> move constructor noexcept? 0

我很想知道,但是,是否可以明确指定 Foo(U&amp;&amp;) 构造函数的 noexcept-ness,采用转发引用(即我需要在注释掉的部分中用什么替换 ?上面的 noexcept 说明符)?

【问题讨论】:

    标签: c++ perfect-forwarding noexcept forwarding-reference


    【解决方案1】:

    你可以这样做:

    template<typename U, typename = 
        std::enable_if_t<std::is_same_v<std::remove_reference_t<U>, T>>>
    Foo(U&& val) noexcept(noexcept(T(std::forward<U>(val))))
        : _val(std::forward<U>(val))
    {}
    

    内层noexceptnoexcept operator,外层是noexcept specifier

    Demo


    我不得不改变 SFINAE 的使用方式,因为在你的构造函数中

    template<typename U>
    Foo(typename enable_if_same<U, T>::type&& val) 
        : _val(std::forward<U>(val))
    {}
    

    无法推断出U 类型(即使可以推断,...::type&amp;&amp; 也不会是转发引用)。

    【讨论】:

    • 谢谢!这似乎可以解决问题。另外,当你说 U 类型不能在我的构造函数中推断出来时,你是什么意思?我上面给出的示例在 g++ 10.1.0 下对我来说编译得很好。
    • @jinscoe123,在您的示例中,从未调用过构造函数。试试Foo&lt;Bar&gt;(Bar{});,你会看到一个错误:couldn't infer template argument 'U'
    • 啊,是的,你是对的。感谢您向我指出这一点!
    • 另外,我刚刚注意到noexcept(std::is_nothrow_constructible_v&lt;T, U&amp;&amp;&gt;) 似乎也可以正常工作。这和你上面的解决方案有什么区别吗?还是他们都完成了同样的事情?
    • @jinscoe123,它们是一样的(如果_val实际上可以从std::forward&lt;U&gt;(val)构造)。
    猜你喜欢
    • 1970-01-01
    • 2020-04-02
    • 2019-11-22
    • 2011-10-19
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多