Как я могу передать арифметический оператор в шаблон? - PullRequest
4 голосов
/ 26 октября 2009

Я хочу как-то объединить такие шаблоны в один:

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" и т. д.).

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

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

Ответы [ 4 ]

7 голосов
/ 26 октября 2009

Возможно, вы могли бы использовать 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, но вы можете написать свою собственную метафункцию if, если вы не можете использовать Boost (шаблон класса, параметризованный двумя типами, и bool, специализированный за истину и ложь).

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

3 голосов
/ 26 октября 2009

Я думаю, что есть усовершенствование решения 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.

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

3 голосов
/ 26 октября 2009

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

#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 голосов
/ 26 октября 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...