推断类型是否可以存储在 boost::variant 中

Deduce if type can be stored in boost::variant

我使用boost::variant已经有一段时间了,但仍有一些问题让我感到困惑。

以下代码未编译,因为我试图将 std::pair<int, int> 存储到 boost::variant,它只能包含 intdoublestd::string。我仍然需要函数 convert,但它应该抛出异常,以防我试图在 boost::variant 中存储它不适合的类型。

#include <iostream>
#include <boost/variant.hpp>
#include <vector>

typedef boost::variant<int, double, std::string> TVar;

template<typename T>
std::vector<TVar> convert(const std::vector<T>& vec) {  
    std::vector<TVar> ret;
    for (size_t t = 0; t < vec.size(); t++) {
        ret.push_back(vec[t]);
    }
    return ret;
}

int main(int arg, char** args) {
    {
        std::vector<double> v = { 3,6,4,3 };
        auto ret=convert(v);
        std::cout << ret.size() << std::endl;
    }
    {
        std::vector<bool> v = { true, false, true };
        auto ret=convert(v);
        std::cout << ret.size() << std::endl;
    }
    {
        std::vector<std::pair<int, int>> v = { std::make_pair<int, int>(5,4) };
        auto ret = convert(v);
        std::cout << ret.size() << std::endl;
    }
    return 0;
}

看来,std::enable_if 可能是解决方案的一部分,但我无法结束它。

The following code is not compiling, as I'm trying to store a std::pair<int, int> to a boost::variant, that can only contain int, double and std::string. Still I need the function convert, but it should throw an exception in case I'm trying to store a type in the boost::variant that it will not fit.

这实际上不是您应该想要的,您应该对自己的行为感到高兴。

如果您试图将类型存储在 boost::variant 中,但它不适合,这是您程序中的一个问题,编译器在编译时会很明显——编译器没有想法如何继续操作,实际上没有合理的代码可供它为此发出。它知道你要存储的类型,它知道你有什么样的变体,它可以看到没有转换。

当编译器拒绝代码并告诉您没有转换时,它会在了解问题后立即告诉您问题。

如果它只是在 运行 尝试转换时抛出异常,这意味着您要等到稍后的测试才能发现问题。这更烦人且代价更高——这意味着编译器知道或应该知道错误的转换问题,但决定掩盖它。

出于这个原因,通常在 C++ 中,程序员会尝试编写代码,使任何常见错误都表现为编译时错误而不是 运行 时错误。 boost::variant 就是怀着这个想法写的。

因此,我认为您应该重新审视问题的前提,并尝试解释您实际尝试做的事情。

这是我的方法,使用 SFINAE。这不是最好的解决方案,因为每次 add/remove 来自 TVar 的类型时都必须更改测试,但它有效:

//Using SFINAE to check if type can be converted into TVar
template<typename T, typename = std::enable_if_t<std::is_same<TVar::types::item, T>::value
    || std::is_same<TVar::types::next::item, T>::value
    || std::is_same<TVar::types::next::next::item, T>::value>>
std::vector<TVar> convert(const std::vector<T>& vec) {
    std::vector<TVar> ret;
    for (size_t t = 0; t < vec.size(); t++) {
        ret.push_back(vec[t]);
    }
    return ret;
}

std::vector<std::nullptr_t> convert(...)
{
    //throw Something
    return{};
}

小说明:

boost::variant 有一个内部类型,称为 types。它基本上是一个编译时链表,其中每个元素的类型在 type 中。

std::enable_if_t的条件是

std::is_same<TVar::types::item, T>::value /*is T same as the first type in variant? */
|| std::is_same<TVar::types::next::item, T>::value /* is T same as second type */
|| std::is_same<TVar::types::next::next::item, T>::value /* is T same as third type */

因此,仅当上述条件之一为 true 时才采用模板 convert,这将适用于 boost::variant.

中的类型