【问题标题】:template member function specialized on pointer-to-data-member专门针对指向数据成员的指针的模板成员函数
【发布时间】:2012-05-21 04:26:52
【问题描述】:

我想定义一个空值静态模板成员函数,该函数将(明确)专门用于指向数据成员的指针,并且对于每个专门化,可能具有不同的返回类型。

它应该返回一些关于每个属性的详细信息,因此我将这个方法称为trait。返回的特征对象类型将被其他模板检查,因此整个机制必须在编译时可用。

到目前为止,我有这样的东西(当然是损坏的代码):

class Foo{
   // some data members
   int a; std::string b; int c;
   // how to declare generic template here?
   // compile-time error should ensue if none of the specializations below is matched

   // specialization for a (aTraitExpr is expanded from macro, so it is OK to repeat it)
   template auto trait<&Foo::a>()->decltype(aTraitExpr){ return aTraitExpr; }
   // specialization for b; the return type will be different than for trait<&Foo::a>
   template auto trait<&Foo::b>()->decltype(bTraitExpr){ return bTraitExpr; }
};

// some code which queries the trait at compile-time
// e.g. supposing all possible trait types declare isSerializable
// which happens to be True for a and False for b

Foo* foo;
template<bool isSerializable> void doSerialization(...);
template void doSerialization<true>(...){ ... };
template void doSerialization<false>(...){ /* no-op */ };

doSerialization<Foo::trait<&Foo::a>()::isSerializable>(...); // -> doSerialization<true>(foo)
doSerialization<Foo::trait<&Foo::b>()::isSerializable>(...); // -> doSerialization<False>(...)
doSerialization<Foo::trait<&Foo::c>()::isSerializable>(...); // -> compile error, specialization Foo::trait<&Foo::c> not defined

能得到一些关于如何实现这一点的提示吗? (我不是想发明一个新的序列化系统,我已经使用了 boost::serialization;每个 trait 中都会有更多信息,这只是一个示例,为什么在编译时需要它。

编辑:我能够得到接近我想要的东西,它显示为at ideone.com。我放弃了trait&lt;Foo::a&gt;()(现在),所以有一个静态函数getTrait_a(),它返回对可修改类型特征的引用,但在编译时部分固定(例如,Foo::TraitType_a::flags 工作)。感谢所有回复的人,很遗憾我只能选择其中一个作为“答案”。

【问题讨论】:

    标签: c++ templates c++11 pointer-to-member


    【解决方案1】:

    定义特征类的常用方法是将结构/类包装在编译时常量表达式周围(而不是包装返回此类表达式的函数)。获取类成员函数的语法如下:

    template
    <
        SomeReturnType (SomeClass::*SomeMemberFunction)(SomeParameters)
    >
    class SomeTrait
    {
        static const value = SomeCompileTimeConstantExpression;
    };
    

    在你的情况下,你会这样做:

    template
    <
        void (Foo::*f)()
    >
    class trait
    {
        static const value = fTraitExpr;
    };
    

    然后,您为class Foo 的所有成员函数专门化此特征:

    template<>
    class trait<&Foo::a>
    {
        static const value = aTraitExpr;
    };
    
    // same for Foo::b and Foo::c
    

    此外,重载函数模板比​​特化它们更惯用:

    template<int V> struct Int2Type { enum { value = V }; };
    
    Foo* foo;
    
    template
    <
        void (Foo::*f)()
    >
    void doSerialization(...)
    {
        dispatch::doSerialization(Int2Type< trait<f>::value >(), ...);
    }
    
    namespace dispatch {
    
    doSerialization(Int2Type< true >, ...) { ... };
    
    doSerialization(Int2Type< false >, ...) { /* no-op */ };
    
    } // namespace dispatch
    

    你可以这样称呼它:

    doSerialization<&Foo::a>(...);
    doSerialization<&Foo::b>(...);
    doSerialization<&Foo::c>(...);
    

    【讨论】:

      【解决方案2】:

      看起来你想要几个重载而不是特化。不幸的是,您没有详细说明 xTraitExpr 是什么,但它似乎只是一个定义了成员 isSerializable 的类型。我可能会这样

      class Foo {
         // your members have been omitted to save space...
      
         template<typename T, T Foo::*M>
         struct W { };
      
         static decltype(aTraitExpr) trait(W<int, &Foo::a>) {
           return aTraitExpr;
         }
      
         static decltype(bTraitExpr) trait(W<std::string, &Foo::b>) {
           return bTraitExpr;
         }
      
         // other overloads for other members...
      
      public:
         // overloads for each member type
         template<int Foo::*M> 
         static decltype(trait(W<int, M>())) trait() { 
           return trait(W<int, M>()); 
         }
      
         template<std::string Foo::*M> 
         static decltype(trait(W<std::string, M>())) trait()  { 
           return trait(W<std::string, M>()); 
         }
      };
      

      trait(W&lt;M&gt;()) 是一个依赖调用。依赖调用在定义和实例化时执行 ADL,仅在定义时执行非限定查找。这就是为什么 W 和使用它的附加 trait 重载必须在 trait 类型重载之前而不是在它们之后定义,否则返回类型和函数体中的解析结果将不同,因为它们被解析在不同的时间(在类定义之后才延迟解析主体,并立即解析返回类型)。

      您可以将trait 设为constexpr 函数,并使xTraitExpr 成为一个文字类,并使用constexpr 构造函数适当地初始化isSerializable,或者您可以按如下方式应用decltype

      doSerialization<decltype(Foo::trait<&Foo::a>())::isSerializable>(...);
      

      【讨论】:

      • xTraitExprTrait&lt;compile-time-params&gt;().modifier1().modifier2() 我需要从中提取编译时参数。很抱歉没有说清楚,我不想把问题复杂化太多。
      【解决方案3】:

      我认为在这里使用函数模板没有意义。话虽如此,使用类模板代替它也不是那么方便:您必须考虑这样一个事实,即非静态数据成员可以具有不同的类型,并且可以有多个具有相同类型的非静态数据成员.这是一种可能性:

      template<typename T>
      struct is_serializable: std::false_type {};
      
      struct Foo {
          int a; std::string b; int c;
      
          // Primary template left undefined on purpose
          // alternatively, could use a static_assert on a dependent
          // std::false_type::value for better diagnostics
          template<typename T, T t>
          struct attribute_trait;
      };
      
      // Define explicit specializations outside of class
      template<>
      struct Foo::attribute_trait<int Foo::*, &Foo::a>
      : is_serializable<int> {};
      
      template<>
      struct Foo::attribute_trait<std::string Foo::*, &Foo::b>
      : is_serializable<std::string> {};
      

      可以用作

      doSerialization<Foo::attribute_trait<decltype(&Foo::a), &Foo::a>::value>(/* stuff */);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-20
        • 1970-01-01
        • 1970-01-01
        • 2010-09-13
        • 1970-01-01
        • 2012-07-05
        • 1970-01-01
        相关资源
        最近更新 更多