Использование noexcept

Для чего используется noexcept в конструкторе перемещения? Насколько я понял, он используется, чтобы вызывать не конструктор перемещения, а копирующий конструктор. Верный ли мои рассуждения?

class String
{
    size_t len;
    char* buffer;
public:
    String(const char* str)
    {
        len = strlen(str);
        buffer = new char[len + 1];
        memcpy(buffer, str, len + 1);

        std::cout << "Call CTR " << str << endl;


    }
    friend std::ostream& operator<< (std::ostream& stream, const String& str);
    ~String()
    {
        delete[] buffer;
    }

    String& operator= (const String& str)
    {
        delete[] buffer;
        if (this == &str)
        {
        }
        this->len = str.len;
        buffer = new char[len + 1];
        memcpy(buffer, str.buffer, len + 1);
        return *this;

        std::cout << "Call COPY " << str << endl;
    }

    char& operator[] (unsigned int index)
    {
        return buffer[index];
    }

    String(const String& str) 
        :
        len(str.len)
    {
        buffer = new char[len + 1];
        memcpy(buffer, str.buffer, len + 1);
    }

    String(const String&& str) noexcept
    {
    
        std::cout << "MOVE СTR\n" << str << endl;
    }

};

std::ostream& operator<< (std::ostream& stream, const String& str)
{
    stream << str.buffer;
    return stream;
}

int main(int argc, char* argv[])
{
    
    String str = "string";
    String str2 (std::move(str));
    return 0;
 }

Но если использовать либо нет noexcept все равно вызывается этот конструктор.


Ответы (1 шт):

Автор решения: Bearded Beaver

Если move-ctor вашего класса не помечен как noexcept, действительно вместо него будет вызываться конструктор копирования, но в одном достаточно узком случае.

Допустим у вас есть вектор, в который вы добавляете объекты вашего класса. В какой-то момент вместимости вектора (размера выделенной под него памяти) перестает хватать и вектор делает переаллокацию - выделяет новую память с запасом и переносит туда содержимое старого фрагмента памяти. И если у вас кидающий перемещающий конструктор, вместо него вектор вызовет конструктор копирования.

Это сделано для того, чтобы сохранить состояние вектора в случае вылета исключения - если мы копировали объекты, можно просто почистить новую память и старое состояние вектора останется валидным, а в случае перемещения, непонятно, что куда возвращать.

Другие контейнеры стандартной библиотеки следуют тому же правилу


upd

Добавлю что наличие noexcept у методов позволяет компилятору лучше оптимизировать исполняемый файл (хотя к конструкторам копирования/перемещения это отношения не имеет).

Также cpp core guidelines рекомендуют всегда помечать move-ctor noexcept так как кидающий перемещающий конструктор "нарушает ожидания большинства людей": http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-move-noexcept

→ Ссылка