Вернуть ссылку на std :: variant, содержащую некопируемые альтернативы - PullRequest
0 голосов
/ 03 августа 2020

Давайте рассмотрим этот код:

#include <iostream>
#include <variant>

struct Foo {
#if 0
    Foo() = default;
    Foo(const Foo&) = delete;
#endif
    void foo() { std::cout << "foo" << '\n'; }
};

using MyVariant = std::variant<Foo>;

MyVariant& getVariant() {
    static MyVariant variant{Foo{}};
    return variant;
}

int main() {
    auto& variant = getVariant();
    auto& foo = std::get<Foo>(variant);
    foo.foo();
}

Он компилирует и печатает «foo».

Если вы измените #if 0 на #if 1, что сделает Foo не копируемым class, то код больше не компилируется.

Почему я не хочу, чтобы Foo нельзя было копировать? Чтобы избежать ошибок «неожиданные копии» с чем-то вроде auto variant = getVariant(); (обратите внимание, что & отсутствует по сравнению с исходным кодом).

Я не могу найти правильный синтаксис для этого кода. Может быть, его нет ... Моя цель - выбрать альтернативу при первом вызове функции, и вариант не будет изменен позже (он всегда будет содержать одну и ту же альтернативу).

Что вы предлагаете ? Спасибо!

1 Ответ

4 голосов
/ 03 августа 2020

Оператор MyVariant variant{Foo{}}; требует, чтобы Foo имел доступный конструктор копирования или перемещения, но его нет. Когда вы delete конструктор копирования, который также делает конструктор перемещения необъявленным , так что вам нужно явно объявить этот конструктор перемещения для компиляции.

auto foo = std::get<Foo>(variant) требуется копия конструктор. Вместо этого используйте ссылку.

Пара этих исправлений для компиляции:

struct Foo {
    Foo() = default;
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default; // <--- Declare move constructor.
    void foo() { std::cout << "foo" << '\n'; }
};

using MyVariant = std::variant<Foo>;

MyVariant& getVariant() {
    static MyVariant variant{Foo{}};
    return variant;
}

int main() {
    auto& variant = getVariant();
    auto& foo = std::get<Foo>(variant); // <--- Use reference.
    foo.foo();
}
...