1

テンプレート引数としてファンクターを提供できるようにしたいと考えています。ファンクターは、その引数として「自分自身」を提供できなければなりません。

私は次のようなものを想像します:

template<typename T, template<typename> class SumFunctor> class foo;

template<typename T>
struct sum_default
{
    foo<T, sum_default> operator()(foo<T, sum_default> a, foo<T, sum_default> b) const 
    {
            T a_data = a.data();
            T b_data = b.data();
            return foo<T, sum_default>(a_data + b_data);
    }
};

template<typename T>
struct sum_awesome
{
    foo<T, sum_awesome> operator()(foo<T, sum_awesome> a, foo<T, sum_awesome> b) const 
    {
            T a_data = a.data();
            T b_data = b.data();
            return foo<T, sum_awesome>(a_data - b_data);
    }
};

template<typename T=int, template<typename> class SumFunctor=sum_default>
class foo
{
private:
    T _data;
    SumFunctor<T> _functor;
public:
    foo<T, SumFunctor>(T data) : _data(data) {}

    T data() { return _data; }

    friend const foo operator +(const foo& lhs, const foo& rhs)
    {
            return lhs._functor(lhs,rhs);
    }
};

int main(){
    foo<> a(42); 
    foo<double> b(1.0);
    foo<double,sum_default> c(4.0);
    foo<double,sum_awesome> d(4.0);

    a+a;
    d+d;
}

これは可能ですか?

別の解決策は、コンストラクターにファンクターを提供することですが、ユーザーが自分でファンクターを動的に割り当てる必要があるため、これは非常に見苦しいと思います (コンストラクターでファンクターのタイプを決定できないためです。RTTI を使用してそうするようにも見えます)。少し醜い):

foo<double> a(42, new sum_default<double>() );

これにより、すべてのファンクターが事前定義された基本ファンクターから派生することも強制されます。

アップデート

テンプレート引数を sum_default テンプレート引数に追加しようとしても、問題が解決しないようです。

template<typename T>
struct sum_default
{
// Error    1   error C3200: 'sum_default<T>' : invalid template argument for template parameter 'SumFunctor', expected a class template
foo<T, sum_default<T> > operator()(foo<T, sum_default<T> > a, foo<T, sum_default<T> > b) const 
{
    T a_data = a.data();
    T b_data = b.data();
    return foo<T, sum_default<T> >(a_data + b_data);
}
};
4

1 に答える 1

3

ここであなたを噛んでいるのは「クラス名インジェクション」として知られています。たとえばFoo<T>、クラステンプレート内では、の無条件の使用Fooは実際にはとして扱われFoo<T>ます。C++11§14.6.1/1を引用:

通常の(非テンプレート)クラスと同様に、クラステンプレートには注入されたクラス名があります。注入されたクラス名は、テンプレート名またはタイプ名として使用できます。template-argument-listとともに、template template-parameterのtemplate-argumentとして、またはfriendクラステンプレート宣言のelaborated-type-specifierの最終識別子として使用される場合、クラステンプレート自体を参照します。 。それ以外の場合は、。で囲まれたクラステンプレートのtemplate-nameの後にtemplate-parametersが続くのと同じです。<>

したがって、の内部ではsum_default<T>foo<T, sum_default>を入力した場合と同じように扱われます(これは、テンプレートテンプレートパラメータが必要な場合foo<T, sum_default<T> >は明らかに機能しません)。foo

この動作を回避するには、それらのクラステンプレート内でのクラステンプレート名の使用を限定する必要があります。クラステンプレートはグローバルスコープに::あるため、十分です。

template<typename T>
struct sum_default;

template<typename T = int, template<typename> class SumFunctor = sum_default>
class foo
{
    T _data;
    SumFunctor<T> _functor;

public:
    foo<T, SumFunctor>(T data) : _data(data) { }

    T data() { return _data; } const

    friend foo operator +(foo const& lhs, foo const& rhs)
    {
        return lhs._functor(lhs, rhs);
    }
};

template<typename T>
struct sum_default
{
    foo<T, ::sum_default> operator ()(foo<T, ::sum_default> a,
                                      foo<T, ::sum_default> b) const
    {
        return foo<T, ::sum_default>(a.data() + b.data());
    }
};

template<typename T>
struct sum_awesome
{
    foo<T, ::sum_awesome> operator()(foo<T, ::sum_awesome> a,
                                     foo<T, ::sum_awesome> b) const
    {
        return foo<T, ::sum_awesome>(a.data() - b.data());
    }
};

int main()
{
    foo<> a(42);
    foo<double> b(1.0);
    foo<double, sum_default> c(4.0);
    foo<double, sum_awesome> d(4.0);

    a + a;
    d + d;
}

fooこれにより、のコンストラクターをこのように定義して、少しのノイズを減らすこともできることに注意してください。

foo(T data) : _data(data) { }
于 2012-04-26T18:38:58.483 に答える