1

次のような任意のメンバーのセットを使用して任意のクラスを作成するテンプレート「AutoClass」を作成しようとしています。

AutoClass<int,int,double,double> a;
a.set(1,1);
a.set(0,2);
a.set(3,99.7);
std::cout << "Hello world! " << a.get(0) << " " << a.get(1) << " " << a.get(3) << std::endl;

これで、ワーキング「セット」メンバーを持つAutoClassができました。

class nothing {};

template <  typename T1 = nothing, typename T2 = nothing, typename T3 = nothing,
            typename T4 = nothing, typename T5 = nothing, typename T6 = nothing>
class AutoClass;

template <>
class AutoClass<nothing, nothing, nothing,
                nothing, nothing, nothing>
{
    public:
    template <typename U> void set(int n,U v){}
};

template <  typename T1, typename T2, typename T3,
            typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
    public:
    T1 V;
    template <typename U> void set(int n,U v)
    {
        if (n <= 0)
            V = v;
        else
            AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
    }
};

そして、対応する「get」の実装で問題が発生し始めました。このアプローチはコンパイルされません:

template <  typename T1, typename T2, typename T3,
            typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
    public:
    T1 V;
    template <typename U> void set(int n,U v)
    {
        if (n <= 0)
            V = v;
        else
            AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
    }
    template <typename W> W get(int n)
    {
        if (n <= 0)
            return V;
        else
            return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
    }
    template <> T1 get(int n)
    {
        if (n <= 0)
            return V;
        else
            return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
    }
};

<nothing, nothing, nothing, nothing, nothing, nothing>その上、私は専門化のためにgetを実装する必要があるようです。これを解決する方法について何かアイデアはありますか?

4

4 に答える 4

3

Boostライブラリの広範な(そして十分にテストされたクロスプラットフォームの)テンプレート魔法のクラスのセットを使用することをお勧めしますか?あなたが探しているのはboost::tupleのようです。独自のコードを記述しないことで逃げることができるときはいつでも(特にテンプレートを使用する複雑な状況では)、他の誰かのコードを使用する必要があります。

于 2010-03-26T17:26:57.653 に答える
3

まず第一に、私はBoost.Fusionを好みBoost.Tupleます。これは、テンプレートメタプログラミングとランタイムアルゴリズムのより良いミックスインをサポートしているからです。

たとえば、私はあなたに少し驚異を提示したいと思います:

struct Name {}; extern const Name name;
struct GivenName {}; extern const GivenName givenName;
struct Age {}; extern const Age age;

class Person
{
public:
  template <class T>
  struct value
  {
    typedef typename boost::fusion::result_of::at_key<data_type const,T>::type type;
  };

  template <class T>
  struct has
  {
    typedef typename boost::fusion::result_of::has_key<data_type,T>::type type;
  };

  template <class T>
  typename value<T>::type
  get(T) { return boost::fusion::at_key<T>(mData); }

  template <class T>
  Person& set(T, typename value<T>::type v)
  {
    boost::fusion::at_key<T>(mData) = v; return *this;
  };

private:
  typedef boost::fusion::map <
    std::pair<Name, std::string>,
    std::pair<GivenName, std::string>,
    std::pair<Age, unsigned short>
  > data_type;
  data_type mData;
};

使うのは本当に楽しいです:

Person p;
p.set(name, "Rabbit").set(givenName, "Roger").set(age, 22);

ええと、私自身、インデックスよりもクラスによるインデックスの方が好きです。なぜなら、タイプチェックを追加するだけでなく、意味を伝えることができるからです;)

于 2010-03-26T18:43:24.337 に答える
1

他の人が述べたように、Boostや他の場所からの既存の実装を再利用することで、おそらくあなたが望む場所にたどり着くことができるはずです。

それらを使用して実行できないことを行う場合、または興味がある場合:

  • 疑似可変個引数テンプレートを実装から除外するようにしてください
  • 代わりにタイプリストを使用して、再帰的なメタ関数などを許可します。
  • 必要に応じて、実装に転送するインターフェイスとして疑似可変個引数テンプレートを使用します
  • コンパイル時に可能な限り多くのことを行います。特に、インデックスなどをチェックします。

利便性のためにMPLを利用する単純なアプローチは、次のようになります。

template<class Types, size_t N> struct holder 
  // recursively derive from holder types:
  : holder<Types, N-1> 
{
    typename boost::mpl::at_c<Types,N>::type value;
};

// specialization that terminates the recursive derivation:
template<class Types> struct holder<Types,0> {
    typename boost::mpl::at_c<Types,0>::type value;
};

template<class Types>
class AutoClass 
  // recursively derive from holder types:
  : holder<Types, boost::mpl::size<Types>::value-1>    
{
    enum { n = boost::mpl::size<Types>::value };
public:
    template<size_t N, class U> void set(const U& u) {
        // index check at compile time:
        BOOST_STATIC_ASSERT((N < n));
        // cast to responsible holder base:
        static_cast<holder<Types,N>*>(this)->value = u;
    }
    template<size_t N> typename boost::mpl::at_c<Types,N>::type get() const { 
        // index check at compile time:
        BOOST_STATIC_ASSERT((N < n));
        // cast to responsible holder base:
        return static_cast<const holder<Types,N>*>(this)->value;
    } 
};

使用法:

typedef boost::mpl::vector<int,std::string> Types;
AutoClass<Types> a;

a.set<0>(42);
assert(a.get<0>() == 42);
a.set<1>("abcde");
assert(a.get<1>() == "abcde");

これは、エンドユーザーの便宜のために疑似可変個引数テンプレートでラップできることに注意してください。

于 2010-03-26T18:28:57.923 に答える
0

基本ケースのため、<nothing、nothing...>を実装する必要があります。検討:

template <typename W> W get(int n)
{
    if (n <= 0)
        return V;
    else
        return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
}

nが5の完全なAutoClassでこの関数を呼び出すとどうなるかを考えてみましょう。この関数は、5つのメンバーで自動クラスを作成し、n=4で呼び出します。

template <typename W> W get(int n) // current autoclass is <T6,nothing,nothing...>
{
    if (n <= 0)
        return V;
    else
        return AutoClass<T2,T3,T4,T5,T6>::get(n-1); // this is <nothing, nothing...>
}

確かに、この自動クラスの呼び出しは発生しませんが、コンパイラーは、指示されているため、とにかくそのコードをコンパイルする必要があります。

nは1093である可能性があるため、AutoClass <nothing、...>::getも作成する必要があります。

私はあなたの現在のインターフェースでこれから抜け出す方法を見ていません。テンプレート引数にnを入れると、これを行わない特殊なケースを作成できます。この場合はできません。解決するのがかなり難しいこのインターフェースを選択したので、あなたは多くの問題に遭遇するだろうと思います。たとえば、Wが'int'であるが、AutoClass :: get(n-1)がdouble以下を返す場合、まったく互換性がない場合はどうなりますか?

于 2010-03-26T17:37:26.857 に答える