【问题标题】:C++ Conditional Templates Compilation based on data type基于数据类型的 C++ 条件模板编译
【发布时间】:2017-01-23 12:10:19
【问题描述】:

我真的希望我可以使用 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 到 string 或 float 到 string 的转换会失败。我在这里真正想要的是一种在类型被检测为 int 而不是任何其他类型时编译代码的 int 部分的方法。 我可以使用模板函数特化或重载,但我认为它真的违背了模板的目的,我可以为 8 种不同类型(例如 List_To_String_Vector、List_To_Int_Vector 等)编写 8 个不同的函数。

我还尝试了另一种技巧,在每个返回类型的地址上使用 reinterpret_cast,然后取消引用它以添加到向量中。这种工作,但有编译器警告,我认为这是未定义的行为。

有没有办法让它正常工作?

谢谢!

【问题讨论】:

  • 这看起来是一个非常糟糕的设计。如果你想让一个对象处理多种类型,那么为什么不使用即将到来的 C++17 标准中的std::any 呢?或者如果你还没有,那么Boost any
  • 为什么你的Func_*Value 的单独函数而不是单个模板或重载集?
  • @Quentin :由于 Legacy 代码,以及执行 Scheme 编程语言对 C++ 的互操作性。
  • @Someprogrammerdude:是的,非常糟糕的设计。我只是想通过它航行。 :( 。我们仅限于 C++ 11(即使那是最近的变化)
  • 哇。这么多好答案!请给我一些时间来分析每个问题并将其中一个标记为答案,尽管我很感谢你们所有人!

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


【解决方案1】:

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&lt;&gt; 之类的东西可以让这更干净。

【讨论】:

  • @PiyushSoni :如果您发布 MVCE 我可以在答案中添加在线演示。
【解决方案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

【讨论】:

    【解决方案3】:

    我也可以玩吗?

    我提出一个基于模板删除转换函数的解决方案

    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&lt;long&gt;()被删除了,没有getTValue(long const &amp;, Object const &amp;)非模板函数。

    ps:注意事项:代码未经测试。

    【讨论】:

      【解决方案4】:

      我建议使用帮助器来推断正确的重载以进行转换:

      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));
      

      不需要模板。

      这确实有一个缺点,即必须重复每个具体类型,即使它们使用相同的转换函数。但是你没有很多。可以使用默认禁用的模板转换运算符来增强重载,但为重用通用转换函数的类型启用。

      【讨论】:

        【解决方案5】:

        我建议使用函数特化来包装每个调用,这样您就可以精确控制每种类型发生的情况,例如

        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;
        }
        

        【讨论】:

          猜你喜欢
          • 2014-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-19
          • 2012-11-03
          • 1970-01-01
          • 1970-01-01
          • 2014-05-10
          相关资源
          最近更新 更多