如果类型可以存储在boost :: variant中,则进行Deduce

时间:2016-06-06 07:14:42

标签: templates c++11 boost-variant

我已经暂时使用boost::variant,但仍有一些问题令我感到困惑。

以下代码未编译,因为我正在尝试将std::pair<int, int>存储到boost::variant,该int只能包含doublestd::string和{{1} }}。我仍然需要函数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可能是解决方案的一部分,但我无法将其结束。

2 个答案:

答案 0 :(得分:4)

  

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

这实际上并不是你想要的东西,你应该对你的行为感到高兴。

如果你试图在boost::variant中存储一个它不适合的类型,那么程序中的一个问题在编译时对编译器来说是显而易见的 - 编译器不知道如何为了继续进行操作,根本没有合理的代码可供它发射。它知道您要存储的类型,它知道您拥有哪种变体,并且可以看到没有转换。

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

如果只是在尝试转换时在运行时抛出异常,那么这意味着在测试之后才会发现问题。这更令人烦恼和昂贵 - 这意味着编译器知道或应该知道转换错误的问题,但决定将其掩盖起来。

通常在C ++中,程序员会尝试编写代码,以便任何常见错误都会显示为编译时错误,而不是运行时错误,因此。 boost::variant写下了这个想法。

所以,我认为你应该重新审视这个问题的前提,并尝试解释你实际上想要做什么。

答案 1 :(得分:1)

这是我的方法,使用SFINAE。这不是最佳解决方案,因为每次从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 */

因此,模板convert仅在上述条件之一为true时才会被采用,而boost::variant中的类型则为domain1