如何在具有模板函数的类中使用类型安全的并集(变量)?

时间:2019-05-28 10:35:31

标签: c++ templates type-conversion unions variant

我想在类中放置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类吗?

2 个答案:

答案 0 :(得分:3)

这里的问题是,您在运行时查询 中存储的当前值,而模板实例化的函数签名是在编译时 进行的。在尝试使用成员函数检索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::optional<double>std::optional<std::string>或类似方法的类似getter的函数,将重载设置传递给std::visit

答案 1 :(得分:2)

所有运行时,即使分支未使用也必须是可编译的。如果我们用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.
}