Search code examples
c++move-constructornoexcept

Implicit move constructor shall be noexcept if possible


Basically per the standard:

An inheriting constructor (12.9) and an implicitly declared special member function (Clause 12) have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions.

Therefore the following code snipped shall have an implicit noexcept move constructor:

template<typename _Tp>
class Foo
{
public:
    Foo() = default;
    explicit Foo(const std::string& s, const std::function<bool(_Tp&)>& f) : stringField(s), funcField(f) {}

private:
    const std::string stringField;
    std::function<bool(_Tp&)> funcField;
};

but unfortunately, it does not:

int main()
{
    std::cout << "std::string: " << std::is_nothrow_move_constructible_v<std::string> << std::endl;
    std::cout << "std::function: " << std::is_nothrow_move_constructible_v<std::function<bool(std::string&)>> << std::endl;
    std::cout << "string_t: " << std::is_nothrow_move_constructible_v<Foo<std::string>> << std::endl;
    return 0;
}

prints

std::string: 1  
std::function: 1  
string_t: 0

using g++ 8.3.0 on Ubuntu 18.04 LTS

Is there something I am missing?


Solution

  • Is there something I am missing?

    Yes. const

    The member Foo::stringField is not std::string, it is const std::string. const std::string is not nothrow move constructible1, and therefore the implicit move constructor of Foo is neither.

    1 Const rvalue cannot bind to a non-const rvalue reference, so move constructor will not be used. Instead, copy constructor is used and copy constructor of std::string is potentially throwing.