2 years ago

#70266

test-img

Adrian Mole

Paradox with default c'tor potentially throwing for a class with a std::string member

Do the C++ "Core Guidelines" prevent (or, at least, strongly discourage) a class with a std::string member from having a default constructor? I ask because of an issue I've noticed when using the MSVC Code Analysis tool (in Visual Studio 2019) in its most 'severe' setting; the issue is the "C26455" warning, as discussed in this document:

The C++ Core Guidelines suggest that default constructors shouldn't do anything that can throw. If the default constructor is allowed to throw, operations such as move and swap will also throw which is undesirable because move and swap should always succeed. Parameterized constructors may throw.

The problem is illustrated in the following, trivial code sample (when compiled using the C++17 Standard – see the "edit" note, below):

#include <string>

class Person {
private:
    std::string my_name;
public:
    // C26455: Default constructor may not throw. Declare it 'noexcept'...
    Person() : my_name{ "Anonymous" } {}

//  // C26447: The function is declared 'noexcept' but calls function 'allocator<char> >()' which may throw exceptions...
//  Person() noexcept : my_name{ "Anonymous" } {}
};

Now, if I heed the generated C26455 warning (as shown in the comment), and add the noexcept specifier to the default constructor (that commented-out in the code sample above), I get a different warning, indicating that my constructor calls a routine in the standard library that may throw:

warning C26447: The function is declared 'noexcept' but calls function 'allocator >()' which may throw exceptions (f.6).

Note that I get the same warning if I remove the initializer list from the constructor and add that default value initializer to the declaration of the my_name member (as std::string my_name{ "Anonymous" };). However, all warnings disappear if I don't explicitly initialize the my_name member – which means I rely on the default constructor for std::string to create an empty object of unspecified capacity.

Presumably, another option would be to provide my own, non-throwing allocator, and pass that as the second argument to the my_name constructor – but this seems, to me, to be a somewhat over-complex solution to what must be a very common use case. (Similar situations would arise when using other standard library container objects as class members that require initialization involving allocation.)

Is there a simple resolution to this issue? Or should I just ignore/disable those code analysis warnings and core guidelines?


EDIT: Note that, when compiling to the C++20 Standard, the C26447 warning goes away when adding the noexcept specifier to the default constructor. I don't understand what C++20 offers that makes this happen; if I did, then maybe that would give a hint as to how to handle the situation for code compiled using the C++17 Standard (as I would prefer).

In my earlier (now deleted) answer, I speculated that it was that the string literal operator (""s) has a constexpr version from C++20 onwards, but it appears that this cannot, in itself, provide the explanation.

c++

constructor

stdstring

0 Answers

Your Answer

Accepted video resources