4

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

它应该返回有关每个属性的一些详细信息,因此我将调用此方法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;每个特征中都会有更多信息,这只是一个示例,为什么在编译时需要它)。

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

4

3 回答 3

3

看起来您想要几个重载而不是专业化。不幸的是,您没有详细说明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<M>())是依赖调用。依赖调用在定义和实例化时执行 ADL,仅在定义时执行非限定查找。这就是为什么使用它W的附加trait重载必须在trait类型重载之前而不是在它们之后定义,否则返回类型和函数主体的解析结果将不同,因为它们在不同时间解析(主体在之后被延迟解析类定义和返回类型立即解析)。

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

doSerialization<decltype(Foo::trait<&Foo::a>())::isSerializable>(...);
于 2012-05-13T09:44:36.540 回答
2

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

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 */);
于 2012-05-13T09:46:12.267 回答
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>(...);
于 2012-05-13T09:37:14.640 回答