【问题标题】:Compiling Class Member Variables Out with C++ Templates?用 C++ 模板编译类成员变量?
【发布时间】:2015-09-17 20:09:39
【问题描述】:

我有一个类似这样的类:

class Compound
{
    void* pValue0;
    void* pValue1;
    void* pValue2;
    void* pValue3;
    void* pValue4;
    void* pValue5;
    void* pValue6;
    void* pValue7;
    void* pValue8;
    void* pValue9;
    void* pValueA;
    void* pValueB;
    void* pValueC;
};

当我创建一个新的 Compound 类时,我分配了额外的内存 [sizeof(Compound) + extraSpace]。每个 pValue 都指向额外内存中的一个地址。

现在,我想根据需要减少 pValue 的数量。模板似乎很合适。

所以如果我想要一个类 Compound,我只想要 pValue0 和 pValueA,然后让编译器删除所有其他 pValue。本质上,我希望它变成:

template <uint Mask = 0A>
class Compound<Mask>
{
    void* pValue0;
    void* pValueA;
}

这可能吗?我接近了 enable_if,但是当我尝试将其限制为特定掩码时,编译器会在 enable_if 情况为假时抛出有关无法找到类型的错误。

谢谢大家!

【问题讨论】:

  • 为什么不是一个带有指针列表的类?
  • 这与将所有值存储在std::vector 中有何不同?您可以随时通过&amp;v[0] 获得pValue0
  • 您可以通过使用empty member optimization 结合std::conditional 来实现您想要的(当条件为假时切换为空类型)。不过那会很丑。
  • @BoPersson,唯一的问题是,在实践中,所有 pValue 都将是指向使用其类型定义的其他类的指针,而不仅仅是 void*。
  • 整数0A与整数A是同一个整数。

标签: c++ templates c++11 compiler-errors


【解决方案1】:

这可能会:

template<char...>
struct flags_tag {constexpr flags_tag(){}; };

template<char...Cs>
struct make_flags{ using type=flags_tag<Cs...>; };
template<char...Cs>
struct make_flags<'0','x',Cs...>:make_flags<Cs...>{};
template<char...Cs>
struct make_flags<'0','X',Cs...>:make_flags<Cs...>{};
template<char...Cs>
using make_flags_t = typename make_flags<Cs...>::type;

template<char...Cs>
constexpr make_flags_t<Cs...> operator""_flag(){ return {}; }

template<char> struct pValue_t;
template<> struct pValue_t<'0'>{ void* pValue0 = 0; };
template<> struct pValue_t<'1'>{ void* pValue1 = 0; };
// ...
template<> struct pValue_t<'A'>{ void* pValueA = 0; };
template<> struct pValue_t<'B'>{ void* pValueB = 0; };
template<> struct pValue_t<'C'>{ void* pValueC = 0; };

template<class flags>
struct Compound;

template<char...Cs>
struct Compound< flags_tag<Cs...> >:
  pValue_t<Cs>...
{};

然后你像这样使用它:

using my_type = Compound< decltype( 0x0A_flag ) >;
int main() {
  my_type test;
  std::cout << test.pValue0 << test.pValueA << '\n';
}

这似乎可以满足您的需求。

我还将禁用 Compound 类型的复制/移动 ctor,并使用 friend 工厂函数使其其他构造函数 private

请注意,此代码可以生成指数级数量的类(2^12 或 4k),这可能会导致二进制膨胀(如果没有内联任何每个类的代码不存在)。

[现场示例]

【讨论】:

  • 这行得通!你能解释为什么这部分是必要的吗?:template&lt;char...Cs&gt; constexpr make_flags_t&lt;Cs...&gt; operator""_flag(){ return {}; } 我不明白为什么这部分特别是必要的。我试过做template&lt;char...Cs&gt; constexpr make_flags_t&lt;Cs...&gt; operator_test(){ return {}; },但那行不通。 _flag 部分有什么必要?
  • @pantaryl 我试图尽可能接近Compound&lt;0A&gt; 语法。一个用户定义的文字_flag,它使用如下整数的字符:0x0A_flag——这就是template&lt;char...&gt; operator"" 所做的。然后我们获取它的类型(包含十六进制字符)并将其提供给Compound&lt; decltype( 0x0A_flag ) &gt;。你可以使用Compound&lt; decltype( operator_test&lt;'0', 'A'&gt;() &gt; 或其他类似的语法,如Compound&lt; flags_tag&lt; '0', 'A' &gt; &gt;
  • 我已将实现更改为link。但是,除非将operator_test 重命名为operator_flag,否则它会拒绝编译。知道为什么吗?
  • @pantaryl 除了拼写错误、未定义的行为、类型不明显、缺少constexpr 关键字、coliru.stacked-crooked.com/a/42077c5aa6556058 之外——它似乎有效?我认为operator_test 甚至不需要存在于那个版本中?而operator""_flagoperator_testoperator_flag 完全不同。请,如果您要向我引用代码,请提供实际生成相关错误的实际代码——我又浪费了 5 分钟来修复您的 pastebin 中与重命名 operator_test 完全无关的错误。
  • 好的,我已经清理了一些。 LINK 请参阅我说的注释掉的部分是问题所在:您知道一种方法可以在成员函数中引用可能的成员变量,但是在 StateMask 无效时编译出逻辑吗?
【解决方案2】:

std::conditional 类似于std::enable_if,除了条件总是返回一个类型。

std::conditional&lt;satisfies(Mask), void*, EmptyClass&gt; 将是有条件地交换成员类型并使其编译的方法。

问题是,C++ 不允许空成员。大小将为 1。要解决此问题,您需要 empty member optimization。这将实现您要求的内存布局,但不幸的是,这将使类成员难以阅读。如果您认为值得追求,您可以添加访问器函数来缓解这种情况。

【讨论】:

    【解决方案3】:

    我假设 Compound 类应包含指向不同类对象的指针,而不是像您在评论中所说的 void 指针。

    我不知道这是否是个好主意,因为我认为设计有问题,但您可以使用 ints 上的元组和模板特化来选择您想要的类型:

    class Class0;
    class ClassA;
    
    template<int> struct Compound {};
    template<> struct Compound<0A> 
    {
        typedef std::tuple<Class0*, ClassA*> type;
    }
    // other mapping from mask to type
    
    typename Compound<0A>::type aCompund(ptr0, ptrA);
    

    【讨论】:

      猜你喜欢
      • 2017-02-19
      • 1970-01-01
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      • 2020-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多