4

データメンバーへのポインタに(明示的に)特化され、特殊化ごとに異なる戻り型を持つことができるnullary静的テンプレートメンバー関数を定義したいと思います。

各属性に関する詳細情報が返されるはずなので、このメソッドを呼び出します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機能します)。返信してくださった皆様のおかげで、残念ながら「答え」としては1つしか選べません。

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コンストラクターを適切に初期化するリテラルクラスにするか、次のように適用できます。constexprxTraitExprisSerializabledecltype

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 に答える