1

多くのデータ構造に対して (別のクラスの演算子 << および >> を介して) いくつかの入出力操作を実行できるクラス テンプレートを定義しようとしています。

要するに、たとえば次のことができます。

vector<int> x;
map<string,vector<vector<int>>> y;
const int z = 42;

FooStream fs;

Foo<decltype(x)> fx(x);
Foo<decltype(y)> fy(y);
Foo<decltype(z)> fz(z);

fs << fx << fy << fz >> fy >> fx;

私の質問は、どうすればこれに到達できるかです:

vector<int> x;
map<string,vector<vector<int>>> y;
const int z = 42;

FooStream fs;

fs << x << y << z >> y >> x;
//error : no operator found which takes a right-hand operand of type 'std::vector<_Ty>' (or there is no acceptable conversion)

FooStream::operator<< と >> は、特殊化 Foo< T> が定義されている場合、型 T を受け入れる必要があります。

詳細については、これが私の(簡略化された)コードです。

class FooBase1{
protected:
    union p_type{const void *p1;void *p2;} p;
public:
    FooBase1(const void *v){p.p1 = v;};
    virtual void func1_() = 0;
};

class FooBase2 : public FooBase1{
public:
    FooBase2(void *v) : FooBase1(v){};
    virtual void func2_() = 0;
};

template<typename T,typename Enable=void>
class Foo{
public:
    typedef void generic;
};

template<typename T,typename E=void> struct is_generic                             : std::false_type{};
template<typename T>                 struct is_generic<T,typename Foo<T>::generic> : std::true_type {};

template<typename T,typename T2=void> struct enable_if_ng  : enable_if<!is_generic<T>::value,T2> {};

template<typename T> struct get_foo_type          {};
template<typename T> struct get_foo_type<Foo<T> > {typedef T type;};

#define DECLARE_FOO_SPECIALIZATION(Type)\
template<>\
class Foo<Type> : public FooBase2{\
public:\
    Foo(Type &v) : FooBase2(&v){}\
    virtual void func1_(){func1(*(const Type *)p.p2);}\
    virtual void func2_(){func2(*(Type *)p.p2);}\
    static void func1(const Type&){/*user defined*/}\
    static void func2(Type&){/*user defined*/}\
};
    //friend FooStream& operator>>(FooStream& fs,Type &f){Foo<Type>(f).func2_();return fs;} doesn't work

DECLARE_FOO_SPECIALIZATION(bool)
DECLARE_FOO_SPECIALIZATION(int)
DECLARE_FOO_SPECIALIZATION(long)
DECLARE_FOO_SPECIALIZATION(float)
DECLARE_FOO_SPECIALIZATION(double)
DECLARE_FOO_SPECIALIZATION(string)

template<typename T1>
class Foo<vector<T1>,typename enable_if_ng<T1>::type> : public FooBase2{
public:
    Foo(vector<T1> &v) : FooBase2(&v){}
    virtual void func1_(){func1(*(const vector<T1> *)p.p2);}
    virtual void func2_(){func2(*(vector<T1> *)p.p2);}
    static void func1(const vector<T1>&){/*user defined*/}
    static void func2(vector<T1>&){/*user defined*/}
};

template<typename T1,typename T2>
class Foo<map<T1,T2>,typename enable_if<!is_generic<T1>::value && !is_generic<T2>::value>::type> : public FooBase2{
public:
    Foo(map<T1,T2> &v) : FooBase2(&v){}
    virtual void func1_(){func1(*(const map<T1,T2> *)p.p2);}
    virtual void func2_(){func2(*(map<T1,T2> *)p.p2);}
    static void func1(const map<T1,T2>&){/*user defined*/}
    static void func2(map<T1,T2>&){/*user defined*/}
};

template<typename T1>
class Foo<const T1,typename enable_if_ng<T1>::type> : public FooBase1{
public:
    Foo(const T1 &v) : FooBase1(&v){}
    virtual void func1_(){func1(*(const T1 *)p.p1);}
    static void func1(const T1& v){Foo<T1>::func1(v);}
};

class FooStream{
public:
    FooStream(){};
    FooStream& operator<<(FooBase1 &f){f.func1_();return *this;};
    FooStream& operator>>(FooBase2 &f){f.func2_();return *this;};
};

(追加の質問: マクロ DECLARE_FOO_SPECIALIZATION をテンプレートを使用してより適切なものに置き換えることはできますか? クラス階層を変更する必要がありますか?)

ありがとうございました。

4

1 に答える 1

1

これEnableIfは多くのコンパイラでは機能しませんが、そこではより粗雑な SFINAE トリックを使用できます。

template<std::size_t n>
struct secret_enum { enum class type {}; };
template<bool b, std::size_t n = 0>
using EnableIf = typename std::enable_if< b, typename secret_enum<n>::type >::type;

template<typename T, bool b=true>
struct can_be_fooed : std::false_type {};
template<typename T>
struct can_be_fooed<T, std::is_same<Foo<T>, Foo<T>>::value>:std::true_type {};

class FooStream{
public:

  template<typename T, EnableIf<can_be_fooed<typename std::decay<T>::type>>...>
  FooStream& operator<<(T&&t){
    // body of <<, possibly involving wrapping the t in a Foo<T>?
  };
};

この種のコードをテストするときは、1 行で 15 のことを行うのをやめてください。最初の 1 つが失敗します。一度に 1 ステップずつテストします。テンプレート メタ プログラミングでは、コンパイル ステップをデバッグします。バイト サイズのチャンクでコンパイルし、各ステップでチェックすることで、コードをステップ実行するのと同じことを行います。


マクロの代わりに、問題の要素が . を持つ唯一のものである特性クラスを作成しますtrue。次に、SFINAE を使用templateして、それらの型に対してのみオンになるものを作成します。コードの行数は同じですが、マクロによって生成されたコードはありません。

template<typename T> struct do_stuff : std::false_type {};
template<> struct do_stuff<int> : std::true_type {};
template<> struct do_stuff<bool> : std::true_type {};
// yada yada

template<typename T>
class Foo<T, typename=typename std::enable_if< do_stuff<T>::value >::type> : public FooBase2 {
public:
  Foo(T &v) : FooBase2(&v){}
  virtual void func1_(){func1(*(const T *)p.p2);}
  virtual void func2_(){func2(*(T *)p.p2);}\
  static void func1(const T&){/*user defined*/}\
  static void func2(T&){/*user defined*/}\
};
于 2013-04-24T03:07:57.370 に答える