C ++条件模板基于数据类型的编译

时间:2017-01-23 12:10:20

标签: c++ c++11 templates vector

我真的希望我能用C ++(11)模板实现这个目标,但我在这里遇到了一些问题。所以我们有一个自定义指针对象,内部可以是以下任何类型:自己类型的对象列表/ int / a char / bool / long / double / char *或任何其他基本类型,它由此对象存储的标志。有一些全局方法可以从此对象获取特定类型的值。

现在,我的目的很简单。我知道,对于我的情况,我的对象是这样的对象的列表,所以我想写一个像这样的函数,因为这是一个常见的场景:

template <typename T>
std::vector<T> List_To_Vector(Object& list)
{
    std::vector<T> vec;
    int listSize = Func_Size(list);    
    for (int i = 0; i < listSize; ++i)
    {
        //let's say this function gives list items one by one
        Object arg = Func_Next(list);
        if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value)
            vec.push_back((T)Func_IntValue(arg));
        else if (std::is_same<T, float>::value || std::is_same<T, double>::value)
            vec.push_back((T)Func_DoubleValue(arg));
        else if (std::is_same<T, std::string>::value || std::is_same<T, char*>::value)
            vec.push_back((T)Func_StringValue(arg)); //Func_StringValue returns char*, so conversion to std::string should work
        else if (std::is_same<T, bool>::value)
            vec.push_back(Func_BoolValue(arg));
        else if (std::is_same<T, char>::value)
            vec.push_back(Func_CharValue(arg));

        vec.push_back(val);
    }

    return vec;
}

int main()
{
    Object listContainingStrings = GetListOfNames();
    Object listContainingNumbers = GetListOfNumbers();
    std::vector<string> vec1 = List_To_STD_Vector<string>(listContainingStrings);
    std::vector<int> vec2 = List_To_STD_Vector<int>(listContainingNumbers);
    return 0;
}

问题是,C ++在这里抱怨,因为它试图编译采用T = std :: string的代码,而int转换为字符串或浮点转换为字符串转换将失败。我真正想要的是一种在类型被检测为int时编译代码的int部分的方法,而不是任何其他类型。 我可以使用模板函数专门化或重载,但后来我认为这真的打败了模板的目的,我可以为8种不同类型编写8个不同的函数(例如List_To_String_Vector,List_To_Int_Vector等)。

我还尝试了另一个hack,使用reinterpret_cast&lt; T *&gt;在每个返回类型的地址上,然后取消引用它以添加到向量。这种方式有效,但有编译器警告,我认为这是未定义的行为。

有没有办法让这项工作正常进行?

谢谢!

5 个答案:

答案 0 :(得分:4)

The fundamental theorem of software engineering

  

我们可以通过引入额外的间接层来解决任何问题。

List_To_Vector做得太多了 - 从Object转换为T,填写vector;抽象出前者,解决方案变得自然。首先,List_To_Vector

template<typename T>
std::vector<T> List_To_Vector(Object& list) {
    std::vector<T> vec;
    for (int i = 0, listSize = Func_Size(list); i < listSize; ++i) {
        vec.push_back(Object_To<T>(Func_Next(list)));
    }
    return vec;
}

现在您可以根据需要重载或专门化Object_To。这是使用SFINAE的一种方式:

// not strictly necessary, but reduces noise a bit
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;

template<typename T>
auto Object_To(Object arg)
 -> enable_if_t<std::is_same<T, int>{} || std::is_same<T, unsigned>{}, T>
{
    return (T)Func_IntValue(arg);
}

template<typename T>
auto Object_To(Object arg)
 -> enable_if_t<std::is_same<T, float>{} || std::is_same<T, double>{}, T>
{
    return (T)Func_DoubleValue(arg);
}

template<typename T>
auto Object_To(Object arg)
 -> enable_if_t<std::is_same<T, std::string>{} || std::is_same<T, char*>{}, T>
{
    return (T)Func_StringValue(arg);
}

template<typename T>
auto Object_To(Object arg) -> enable_if_t<std::is_same<T, bool>{}, T>
{
    return Func_BoolValue(arg);
}

template<typename T>
auto Object_To(Object arg) -> enable_if_t<std::is_same<T, char>{}, T>
{
    return Func_CharValue(arg);
}

使用像boost::fusion::map<>这样的东西,如果能负担得起依赖,可以使这个更加清晰。

答案 1 :(得分:2)

让我们在List_To_Vector函数下面一级。我看到的问题是你有一组不相关的Func_*Value函数,所以让我们一劳永逸地在一个类型感知模板下收集它们:

template <class>
struct valueGetter;

template <> struct valueGetter<float>  { static constexpr auto &get = Func_DoubleValue; };
template <> struct valueGetter<double> { static constexpr auto &get = Func_DoubleValue; };
template <> struct valueGetter<int>    { static constexpr auto &get = Func_IntValue;    };
// etc.

现在List_To_Vector变得微不足道了:

template <typename T>
std::vector<T> List_To_Vector(Object& list)
{
    std::vector<T> vec;
    int listSize = Func_Size(list);    
    for (int i = 0; i < listSize; ++i)
        vec.push_back(valueGetter<T>::get(Func_Next(list)));

    return vec;
}

See it live on Coliru

答案 2 :(得分:1)

我也可以玩吗?

我提出了一个基于模板删除转换功能的解决方案

template <typename T>
T getTValue (T const &, Object const &) = delete;

和一些具有相同签名的非模板转换函数,用于调用正确的Func_X_Value()函数;

之类的东西
int getTValue (int const &, Object const &  obj)
 { return Func_IntValue(arg); }

unsigned int getTValue (unsigned int const &, Object const &  obj)
 { return Func_IntValue(arg); }

float getTValue (float const &, Object const &  obj)
 { return Func_DoubleValue(arg); }

double getTValue (double const &, Object const &  obj)
 { return Func_DoubleValue(arg); }

char * getTValue (char const * &, Object const &  obj)
 { return Func_StringValue(arg); }

std::string getTValue (std::string const &, Object const &  obj)
 { return Func_StringValue(arg); }

char getTValue (char const &, Object const &  obj)
 { return Func_CharValue(arg); }

bool getTValue (bool const &, Object const &  obj)
 { return Func_BoolValue(arg); }

第一个参数未使用,仅用于选择正确的非模板函数,因此for周期变为

for (int i = 0; i < listSize; ++i)
   vec.push_back(getTValue(T(), arg));

引入模板删除功能是为了避免不必要的类型转换(例如:从short intint)并在编译阶段施加错误,如果有人试图调用List_To_Vector()错误T

因此,举例来说,请致电

std::vector<int> vi = List_To_Vector<int>(listContainingNumbers);

应该没问题,但请致电

std::vector<long> vl = List_To_Vector<long>(listContainingNumbers);

因为getTValue<long>()被删除,而且getTValue(long const &, Object const &)没有模板功能。

p.s。:警告:代码未经过测试。

答案 3 :(得分:0)

我建议使用助手来推断转换的正确重载:

class convert
{
    const Object& from;
public:
    explicit convert(const Object& from): from(from) {}

    operator char()        const { return Func_CharValue(from); }
    operator bool()        const { return Func_BoolValue(from); }
    operator std::string() const { return Func_StringValue(from); }
    operator const char*() const { return Func_StringValue(from); }
    // ...
};

// ...

vec.push_back(convert(arg));

无需模板。

这确实有一个缺点,即即使它们使用相同的转换函数也必须重复每种具体类型。但是你没有那么多。可以使用默认情况下禁用的模板转换运算符来增强重载,但是对于重用公共转换函数的类型启用了该重载。

答案 4 :(得分:0)

我建议使用函数特化来包装每个调用,这样你就可以对每种类型进行精确控制,例如。

template<typename T> T object_to(const Object& arg) { }

template<> int          object_to(const Object& arg) { return Func_IntValue(arg); }
template<> unsigned int object_to(const Object& arg) { return Func_IntValue(arg); }
template<> std::string  object_to(const Object& arg) { return Func_StringValue(arg); }
template<> float        object_to(const Object& arg) { return Func_DoubleValue(arg); }
template<> double       object_to(const Object& arg) { return Func_DoubleValue(arg); }
template<> bool         object_to(const Object& arg) { return Func_BoolValue(arg); }
template<> char         object_to(const Object& arg) { return Func_CharValue(arg); }

然后给你的Object类一些标准算法方法并将其插入以下内容:

template<typename T>
std::vector<T> to_vector(const object_list& obj_list) {
    std::vector<T> vec(obj_list.size());
    std::transform(obj_list.begin(),obj_list.end(),vec.begin(),[](const Object& obj) {
        return object_to<T>(obj);
    });
    return vec;
}
相关问题