2009-10-26 3 views
4

Я хочу, чтобы каким-то образом объединить шаблоны, как они в один:Как передать арифметический оператор шаблону?

template <class Result, class T1, class T2> 
class StupidAdd 
{ 
public: 
    T1 _a; T2 _b; 
    StupidAdd(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return _a+_b; } 
}; 

template <class Result, class T1, class T2> 
class StupidSub 
{ 
public: 
    T1 _a; T2 _b; 
    StupidSub(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return _a-_b; } 
}; 

(а затем и тот же код для Mul, Div и т.д.), где весь код является тем же самым, за действительное «+», за исключением, «-» (и «StupidAdd», «StupidSub» и т. д.).

Эти глупые «функторы» затем используются другим шаблоном. Как избежать повторения, БЕЗ препроцессора? (Причина, по которой я попал в шаблоны, заключалась в том, чтобы избежать препроцессора)

То есть, как я могу передавать арифметические операторы в шаблон?

+0

Вы пытаетесь перегрузить эти операторы или это просто пример? – Jacob

+0

Я не перегружаю арифметические операторы - я просто хочу иметь возможность передавать их в шаблоне, что-то вроде template <класс Результат, класс A, класс B, класс ArithOp> Результат DoWork (A a, B b) {return ArithOp (a, b); } ... без определения таких глупых вещей, как StupidAdd, StupidSub – OldCoder

ответ

7

Возможно, вы можете использовать std::plus<T>, std::minus<T>, std::multiplies<T> и std::divides<T>. Однако они будут работать только в том случае, если оба операнда одного типа или, возможно, если левый можно преобразовать в тип первого.

Я не вижу никакого способа добиться того, что вы пытаетесь сделать, за исключением использования препроцессора. Любые веские причины для нежелания макросов?

Если вы хотите, чтобы убедиться, что тип возвращаемого достаточно велик, чтобы содержит результат, вы могли бы сделать что-то по этому пути:

#include <functional> 
#include <boost/mpl/if_.hpp> 

// Metafunction returning the largest type between T and U 
// Might already exist in Boost but I can't find it right now...maybe 
// boost::math::tools::promote_args 
template <typename T, typename U> 
struct largest : 
    boost::mpl::if_< 
     boost::mpl::bool_<(sizeof(T) > sizeof(U))>, 
     T, 
     U 
    > 
{}; 

template <typename T, typename U, template <typename S> class Op> 
struct Foo 
{ 
    typedef typename largest<T, U>::type largeType; 

    largeType bar(const T & t, const U & u) 
    { 
     return Op<largeType>()(t, u); // Applies operator+ 
    } 
}; 

int main() 
{ 
    Foo<int, double, std::plus> f; 
    double d = f.bar(12, 13.0); // takes int and double, returns double 
} 

Здесь я использовал Boost MPL написать largest metafunction, но вы могли бы напишите свой собственный if metafunction, если вы не можете использовать Boost (шаблон класса, параметризованный двумя типами и bool, специализированный для истинных и ложных).

Чтобы определить тип возвращаемого выражения, вы также можете взглянуть на boost::result_of, который, если я правильно понимаю, эквивалентен предстоящему оператору decltype в C++ 0x.

+0

Я не могу - я в зависимости от того, что оператор + (int, double) возвращает double. std :: plus требует, чтобы оба аргумента были одного типа. – OldCoder

+0

Моя основная причина ненавидеть «cpp» заключается в том, что он делает отладку живого ада - момент, когда вы начинаете использовать макросы, - это момент, когда вы теряете способность делать пошаговые операции внутри gdb ... – OldCoder

+0

Я просто редактировал отражают этот факт :). Ну, тогда я не вижу никакого способа сделать это. Если не удастся принять адрес встроенного оператора, в котором я сомневаюсь. –

3

Спасибо, Люк, это очень здорово. я, наконец, сделал это более простым способом:

#include <functional> 

template < 
    class Result, 
    class T1, 
    class T2, 
    template <class ReturnType> class BinaryOp> 
class Stupido 
{ 
public: 
    T1 _a; T2 _b; 
    Stupido(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return BinaryOp<Result>()((Result)_a,(Result)_b); } 
}; 

И использовали «плюс», «минус» при инстанцировании Stupido. Приведение к «Результату» было достаточно для моих потребностей (int + double => double + double => double)

0

Я бы использовал определение типа C++ 0x и новое определение auto. Вам все равно нужно будет определить классы, но с их помощью вы можете лучше определить их. Они все равно будут работать столько, чтобы определить, но по крайней мере с использованием их будет значительно чище. В частности, вы можете использовать decltype/auto, чтобы вывести правильный тип возвращаемого значения, а не указывать его явно.

Они доступны с большим количеством недавних компиляторов - Intel C++, g ++, Comeau, бета-версии VC++ 2010 и даже самой последней итерации Borland/Inprise/Embarcadero/нового названия этой недели.

3

Я думаю, что есть улучшение решения OldCoder в:

#include <functional> 

template <class Result, 
      template <class Result> class BinaryOp> 
struct Stupido 
{ 
    template <typename T1, typename T2> 
    Result operator()(const T1& a, const T2& b) { return BinaryOp<Result>()((Result)a,(Result)b); } 
}; 

Таким образом, вызов может быть выполнен в виде:

Stupido<int, std::plus > stup; 
int result = stup(3.0f, 2.0); 

и тот же функциональный объект может быть использован с несколькими операндами, так что это может быть передан в вызов std::transform.

Я считаю, что должен быть способ удалить один результат из объявления шаблона, но я не могу его найти.

+3

template <класс Результат, шаблон класс BinaryOp> struct Stupido {...}; –

Смежные вопросы