boostのtype_traitsを使ってテンプレートを切り替えたい

解決


Gensai  2008-05-19 18:12:43  No: 68362  IP: 192.*.*.*

開発環境はVisualC++2005ExpressEditionです。
boost version1.34.1を使ってます。

現在、テンプレート引数がPOD型か非POD型かによって切り替わるクラステンプレートを作っています。何とか下記のように出来上がりました。

template<typename T,typename = void>
class MyClass;

//非POD特化版
template<typename T>
class MyClass<T,typename boost::disable_if<boost::is_pod<T> >::type>
{
  public:
    MyClass();
    ~MyClass();
};

//POD特化版
template<typename T>
class MyClass<T,typename boost::enable_if<boost::is_pod<T> >::type>
{
  public:
    MyClass();
    ~MyClass();
};


//Constructor(非POD特化版)
template<typename T>
MyClass<T,typename boost::disable_if<boost::is_pod<T> >::type>::MyClass()
{
  std::cout << "Constructor(NotPOD)\n";
}

//Constructor(POD特化版)
template<typename T>
MyClass<T,typename boost::enable_if<boost::is_pod<T> >::type>::MyClass()
{
  std::cout << "Constructor(POD)\n";
}

//Destructor(非POD特化版)
template<typename T>
MyClass<T,typename boost::disable_if<boost::is_pod<T> >::type>::~MyClass()
{
  std::cout << "Destructor(NotPOD)\n";
}

//Destructor(POD特化版)
template<typename T>
MyClass<T,typename boost::enable_if<boost::is_pod<T> >::type>::~MyClass()
{
  std::cout << "Destructor(POD)\n";
}

HRESULT  Test(int argc,TCHAR const* argv[])
{
  argc;argv;
  
  MyClass<unsigned int>  obj;
  MyClass<std::string>  obj2;
  return S_OK;
}

Testの実行結果

Constructor(POD)
Constructor(NotPOD)
Destructor(NotPOD)
Destructor(POD)


一応できたのですが、切り替えたいのはメソッドの実装のみであって、クラス定義の部分はまったく同じです。だから、クラス定義の部分を下記のように一つにまとめれないかと思ったのですが

template<typename T>
class MyClass<T,void>
{
  public:
    MyClass();
    ~MyClass();
};

そうすると、POD特化版非POD特化版それぞれのConstructorとDestructor計4つのメソッドがすべてC2039(メンバではないというエラー)で怒られました。

なんとかスマートに記述する方法はないでしょうか?
よろしくお願いします。

編集 削除
_michi  2008-05-19 18:16:32  No: 68363  IP: 192.*.*.*

#includeを使ってコードの重複を避けることはできたのですが、
使わずに済む方法ってあるでしょうか?

編集 削除
tetrapod  2008-05-21 10:42:57  No: 68364  IP: 192.*.*.*

> なんとかスマートに記述する方法はないでしょうか?
俺にとってのスマートと元発言者様のスマートと赤の他人のスマートとは違うだろうが
俺ならこう実装する

#include <boost/type_traits.hpp>
#include <iostream>

template<typename T, bool is_pod_type=boost::is_pod<T>::value >
struct x {
    x() { std::cout << "non POD x::x\n"; }
    ~x() { std::cout << "non POD x::~x\n"; }
};

template<typename T>
struct x<T,true> {
    x() { std::cout << "POD x::x\n"; }
    ~x() { std::cout << "POD x::~x\n"; }
};

int main() {
    {x<int> x0;}
    {x<int*> x1;}
    {x<std::ifstream> x2;}
    return 0;
}

編集 削除
tetrapod  2008-05-21 10:45:55  No: 68365  IP: 192.*.*.*

追記

x<int, false> と使われると意味が無いわけで
実用に供するコードではもちろん is_pod_type を外から与えないように修正するよ

編集 削除
_michi  2008-05-21 18:46:24  No: 68366  IP: 192.*.*.*

すいません、Gensaiは別のところで使っていたHNです。

terapodさんありがとうございます。

やっぱり重複するコードをまとめるのは難しいのでしょうか。

いろいろ試してみたのですが、

クラス定義が<T,void>の特化版のみだと、
クラステンプレートを具体化する以前に、<T,disable_if<is_pod>>,<T,disable_if<is_pod>>の特化版メソッドがC2039で怒られます。

クラス定義を<T,void>,<T,disable_if<is_pod>>,<T,disable_if<is_pod>>
の3つとも書くと、テンプレートを具体化したときに
<T,void>の特化版が2つあると怒られてしまいます。

私は、<T,disable_if<is_pod>>,<T,disable_if<is_pod>>のコードの中で重複する部分を<T,void>にまとめられると思っていたのですが、不可能な気がしてきました...。

編集 削除
tetrapod  2008-05-21 23:00:23  No: 68367  IP: 192.*.*.*

えーと・・・何がしたいのかよくわからんのだけど

template<typename T>struct A { ... }; があるとき、特殊化
template<>struct A<int> { ... }; を行うということは、
・ A<int> というクラスを作る
・ A<int> と A<generic> は無関係なクラスである
というプログラマの意思表明と解釈されることになっているわけで
A<int> と A<generic> とで中身がまったく異なるものであってよい。
逆に言えば「そっくり」にしたいのであれば、そういうふうに実装する必然がある。

「そっくり」ではなく「同じ」にしたいのであれば template の特殊化でない方法で実装すればよくて
たとえばこんな感じに実装することは不可能ではない

template<typename T> struct x_workhorse {
    x_workhorse(const boost::true_type& ispod) { std::cout << "X::X(POD)\n"; dumptype(); }
    x_workhorse(const boost::false_type& ispod) { std::cout << "X::X(non-POD)\n"; dumptype(); }
    void dumptype() const { std::cout << typeid(T).name() << std::endl; }
};

template<typename T> struct x : public x_workhorse<T> {
    x() : x_workhorse<T>(typename boost::is_pod<T>::type()) {}
};

int main() {
    {x<int> x0;}
    {x<boost::is_pod<int> > x1;}
    return 0;
}

編集 削除
_michi  2008-05-22 19:51:19  No: 68368  IP: 192.*.*.*

Tが非POD型の時、
boost::disable_if<boost::is_pod<T> >::type
がvoidになり、

TがPOD型の時、
boost::enable_if<boost::is_pod<T> >::type
がvoidになりますよね。

なので<T,void>特化版のコードを書いておけば
PODと非PODどちらにたいしても有効なコードになると思ったのですが。

なぜこんなことがしたいかというと<T,disable_if<is_pod>>特化版と<T,disable_if<is_pod>>特化版はクラス定義からメソッドの実装までほとんどいっしょだからです。一部のメソッドの実装のみが違うだけです。共通する部分を<T,void>特化版として書けないかと。

編集 削除
tetrapod  2008-05-23 09:18:07  No: 68369  IP: 192.*.*.*

特殊化しない template と、特殊化した template は別物だから
同じになる必然があるならプログラマがそのように実装しなきゃならんわけで

0.共通部分を別ファイルにくくりだし、一般と特殊化とでその同一ファイルを
#include する→既に試したんだよね

1.オーバーロードは出来る→俺が既に挙げた
共通な部分はフツーに実装し、共通でない部分はオーバーロードで実装

2.オーバーライドできる→共通部分を基底クラスにもっていく
template<typename T> struct A_common {
    static void dumpfunc() { std::cout << typeid(T).name() << std::endl; }
};

template<typename T, typename enabled=void> struct A : public A_common<T> {
    typedef A_common<T> base_type;
    A() { std::cout << "generic A::A is used on "; base_type::dumpfunc(); }
};

template<typename T>struct A<T, typename boost::enable_if<boost::is_pod<T> >::type> : public A_common<T> {
    typedef A_common<T> base_type;
    A() { std::cout << "pod A::A is used on "; base_type::dumpfunc(); }
};

どれでもいいんぢゃないの?
俺でも、考えるのが面倒になったら0で逝く可能性があるよ

編集 削除
_michi  2008-05-23 22:22:29  No: 68370  IP: 192.*.*.*

う〜ん、やっぱりコードをまとめることは難しいんですね。

0の方法でいくことにします。

最後にtetrapodさん本当にありがとうございました。

編集 削除