2012-05-13 12 views
4

ポインタをデータメンバに(明示的に)特殊化するnullary staticテンプレートメンバ関数を定義したいと思います。戻り値の型。データメンバへのポインタに特化したテンプレートメンバ関数

各属性についての詳細情報が返されるはずですので、このメソッドをtraitと呼びます。返される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を使用しています;それぞれの特性にもっと多くの情報がありますが、これはコンパイル時に必要な例です)。

編集:私が望むものに近づくことができました。at ideone.comと表示されています。私はtrait<Foo::a>()(現在は)をあきらめていますので、コンパイル時に部分的に固定されている(例えばFoo::TraitType_a::flagsが動作するように)変更可能な型の特性への参照を返す静的関数getTrait_a()があります。答えがみんなのおかげで、残念ながら私は答えの一つを "答え"として選ぶことができます。

答えて

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タイプのオーバーロードの前に定義する必要があります。また、戻り値の型と関数のボディの解像度の結果は、異なる時間に解析されるため異なります。後でクラス定義の後で解析され、戻り値の型はすぐに解析されます)。

あなたはtraitconstexpr機能を作り、xTraitExprがconstexprのコンストラクタが適切isSerializableを初期化するとクラスリテラル可能にする、または

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

として使用可能であるどの

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

'と私は抽出する必要があります:。。ここでは可能性がありますそこからのコンパイル時params。申し訳ありませんが、私はその質問をあまり複雑にしたくありませんでした。 – eudoxos

1

特性クラスを定義する一般的な方法は、コンパイル時定数式の周りにstruct/classをラップすることです(そのような式を返す関数をラップすることではありません)。クラスのメンバ関数を取るための構文は次のようである:あなたのケースでは

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

を次のように私はそれが意味がないと思うdecltypeを適用することができしますかここで関数テンプレートを使用してください。つまり、クラステンプレートを代わりに使用することは便利ではありません。非静的データメンバーが異なるタイプを持つことができ、同じタイプの複数の非静的データメンバーが存在する可能性があることを考慮する必要があります。 modifier2() `xTraitExpr`は`形質<コンパイル時-のparams>()であるmodifier1()

doSerialization<Foo::attribute_trait<decltype(&Foo::a), &Foo::a>::value>(/* stuff */); 
関連する問題