Функторы шаблонов, которые должны предоставлять себя в качестве аргументов шаблона

Я бы очень хотел иметь возможность предоставить функтор в качестве аргумента шаблона. Функторы должны иметь возможность предоставить «себя» в качестве этого аргумента.

Я представляю что-то вроде этого:

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);
}
};

person user1202032    schedule 26.04.2012    source источник
comment
Спасибо за ваш ответ, но то, что вы предлагаете, не будет компилироваться.   -  person user1202032    schedule 26.04.2012
comment
Ok. Я добавил ошибку в вопрос. Если бы вы могли помочь в том, как исправить мой порядок определения, я был бы очень признателен.   -  person user1202032    schedule 26.04.2012
comment
Извините, я изначально неправильно понял намерение вашего кода. Извините за шум. :-]   -  person ildjarn    schedule 26.04.2012


Ответы (1)


То, что вас здесь кусает, известно как «инъекция имени класса» внутри шаблона класса, например. Foo<T>, неквалифицированное использование Foo фактически рассматривается как Foo<T>. Ссылаясь на С++ 11 §14.6.1/1:

Как и обычные (нешаблонные) классы, шаблоны классов имеют внедренное имя класса. Имя введенного класса можно использовать как имя-шаблона или имя-типа. Когда он используется с списком-аргументов-шаблона, в качестве аргумента-шаблона для параметра-шаблона шаблона или в качестве окончательного идентификатора в определенном-спецификаторе-типа объявления шаблона дружественного класса он ссылается на сам шаблон класса. В противном случае он эквивалентен имя-шаблона, за которым следуют параметры-шаблона шаблона класса, заключенные в <>.

Следовательно, внутри 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) { }
person ildjarn    schedule 26.04.2012