【问题标题】:Template struct as array模板结构作为数组
【发布时间】:2018-03-05 21:16:34
【问题描述】:

是否可以使用模板类型字段制作结构数组?

template<typename T>
struct MyStruct {
    T *pField;
};

MyStruct< ?? > mystruct_arr[] = {
        { pFieldOfType1 },
        { pFieldOfType2 },
};

以上显然行不通,但是用其他技术可以吗?

我正在尝试遍历数组 mystruct_arr 并在每个结构行上调用此函数:

template<typename T>
void SetupField(T &pSourceField, ...)
{
    Base *field = ...->findBaseFieldFromDatabase(...);
...
    pSourceField = static_cast<T>(field);
...
}

原因是尝试重构一段非常重复的代码,我必须根据几个不同的参数静态转换一长串不同类型的代码,而不会使其过于复杂。

【问题讨论】:

  • MyStruct 中只包含一个指针,如果你想达到这个目的,为什么不考虑使用 void* 呢?
  • 假设你设法声明和初始化mystruct_arr。你会如何使用它?
  • 伊戈尔的问题是关键。因为 ypur 对它的回答决定了如何解决您的问题。您在上面所做的声明没有可观察到的行为;一个空文件符合这些要求。指定准确需要的内容,或许可以找到解决方案。求月,或含糊地说“我只是想知道”,答案是否定的。
  • @Yakk true,更新了我的问题以包含更多我想要做的事情。

标签: c++ arrays templates struct types


【解决方案1】:

模板不是classstruct。它可以被视为模板实例化时创建的classstruct 的蓝图或配方。

只有在实例化时,模板才会成为实际的classstruct,通过指定所需的模板参数:

MyStruct<int>

现在你有一个真实的、活生生的、会呼吸的class。但是MyStruct&lt;int&gt;MyStruct&lt;char&gt; 完全不同。在不指定模板参数的情况下,MyStruct 不是类、结构或任何占用单个 RAM 字节的东西。它只是一些structclass 的模板。

但是使用模板参数,例如MySutrct&lt;int&gt;,这变成了一个带有字段的实际类,也许还有方法。现在你有了一个类,你当然可以有一个数组,现在:

MyStruct<int> mystruct_arr[] = {

};

或者你可以有一个不同的MyStruct&lt;char&gt;s数组:

MyStruct<char> mystruct_arr2[] = {

};

但是你不能有一个包含这两个的数组,因为你不能有一个包含不同类型和类的大杂烩的数组。数组不能同时包含 chars、ints、floats、指针或各种类。数组始终包含相同类型/类的值。所以选择一个特定的MyStruct&lt;whatever&gt;,然后用它制作一个数组,这就是你所能做的。

但你现在也可以声明另一个结构:

struct many_structs {
     MyStruct<int> int_struct;
     MyStruct<char> char_struct;

     // ...
};

这种开始看起来像你想要的数组。但它不是一个数组。只是一个普通的struct;而不是使用数组索引来访问特定的模板实例,而是直接引用结构成员。

您可以通过一些额外的工作为您的结构专门化std::get,并使该结构看起来像一个数组。但是现在你才意识到你重新发明了std::tuple,并且可以简单地做到这一点:

std::tuple<MyStruct<int>, MyStruct<char>> mystruct_tuple;

最后,只有通过一些额外的工作和支持 C++17 的 C++ 编译器,通过声明一个包含 std::anys 或 @987654322 的数组,才能最接近您尝试做的事情@s。如果数组应该只包含模板实例的有限枚举,std::variant 提供了最大的类型安全性和便利性:

std::variant<MyStruct<int>, MyStruct<char>> mystruct_arr[]={

};

生成的数组仅包含这两个特定的模板实例。使用std::any,螺丝会进一步松动,但您必须做更多工作才能使用和访问数组中的每个值。

【讨论】:

    【解决方案2】:

    你认为你想要一个模板数组。

    您真正想要的是可以调用特定模板函数的类型数组。

    第一个是不可能的。第二种在 C++ 中称为类型擦除。

    template<class T>
    using setup_fptr=void(*)(T &, Foo)
    using gen_setup_ptr=void(*)(void*, Foo);
    
    template<class T>
    setup_ptr<T> get_setup(){ return SetupField<T>; }
    template<class T>
    gen_setup_ptr get_gen_setup(){
      return [](void* p, Foo f){ get_setup<T>( *static_cast<T*>(p), f ); };
    }
    struct can_setup {
      void* ptr=0;
      gen_setup_ptr f=0;
    
      can_setup(can_setup const&)=default;
      can_setup& operator=(can_setup const&)=default;
      can_setup()=default;
      explicit operator bool() const{return f;}
    
      template<class T>
      can_setup(T* pt):
        ptr(pt),
        f( get_gen_setup<T>() )
      {}
      void setup( Foo foo ) const {
        f(ptr, foo );
      }
    };
    

    存储can_setup 的数组。循环调用.setup(foo)

    Foo 是您正在使用的任何其他参数的占位符。

    这种技术称为类型擦除;我们忘记(删除)关于T 的一切,除了我们可以setup 它。

    【讨论】:

    • 您的帮助似乎可以解决我的问题,但我在转换 lambda return [](void* p, Foo f){ get_setup( *static_cast(p), f); };转换为 c++ 98 兼容代码。有什么想法吗?
    • @fred template&lt;class T&gt;void gen_setup(void*p, Foo f){ get_setup&lt;T&gt;( *static_cast&lt;T*&gt;(p), f ); }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多