Как я могу использовать безопасные по типу объединения (варианты) внутри класса с шаблонными функциями? - PullRequest
4 голосов
/ 28 мая 2019

Я хотел бы разместить std::variant внутри класса и вернуть его элементы с помощью функции шаблона.Вот пример:

#include <string>
#include <variant>
class Class {
   public:
    std::variant<std::string, double> cont;
    Class() {}
    template <class V> Class(const V v) { cont = v; }
    template <typename V> V fun() {
        if (std::holds_alternative<double>(cont))
            return std::get<double>(cont);
        else if (std::holds_alternative<std::string>(cont))
            return std::get<std::string>(cont);
    }
};

int main() {
    Class c;
    c = 20;
    double d = c.fun<double>();
    return 0;
}

Я пытаюсь вернуть элементы класса Class через функцию шаблона fun.Однако gcc-9.1 отказывается компилировать код и сообщает мне

Class.cpp:12:46: error: cannot convert ‘std::__cxx11::basic_string<char>’ to ‘double’ in return
   12 |             return std::get<std::string>(cont);

Почему есть попытка преобразовать string (второй возвращаемый тип функции foo) в double?Могу ли я предотвратить это и решить проблему?Использую ли я класс std::variant unidiomatic?

Ответы [ 2 ]

3 голосов
/ 28 мая 2019

Проблема здесь в том, что вы запрашиваете текущее значение, хранящееся во время выполнения , тогда как сигнатура функции экземпляра шаблона выполняется во время компиляции .Рассмотрите, как выглядит функция-член, когда вы пытаетесь использовать ее для получения double:

double fun() {
    if (/* ... */)
        return std::get<double>(cont); // Ok return type is double, too
    else if (/* ... */)
        return std::get<std::string>(cont); // Error, return type should be string?!
}

Это не может работать.Вам нужно изменить способ доступа к элементу данных, например, передавая перегрузку, установленную на std::visit, предоставив две функции, подобные геттеру, возвращающие std::optional<double> и std::optional<std::string> или что-то подобное.

2 голосов
/ 28 мая 2019

Все время выполнения, если ветки должны быть скомпилированы, даже если они не приняты.Если мы вызываем fun() с V == double, то возвращение std::string не имеет смысла и вызывает ошибку (даже если эта ветвь никогда не будет принята, компилятор не может знать это наверняка).

Вместо этого просто верните его сразу через V:

template <typename V> V fun() {
    if (std::holds_alternative<V>(cont))
        return std::get<V>(cont);
    return {}; // return default constructed V. You could throw an exception here instead etc.
}
...