LINUX.ORG.RU

Заготовка шаблонных методов

 , ,


0

1

Есть класс:

class foo{
    template <typename Struct, typename Handler>
    void add2Base(const Data &data);
}

Я бы хотел заготовить на его основе методов для заранее известных типов.
Т.е вместо этого:

class foo{
    template <typename Struct, typename Handler>
    void add2Base(const Data &data);
}

foo A;
A.add2Base<testStruct, testHandler>(data);
хочется подобного:
class foo{
    template <typename Struct, typename Handler>
    void add2Base(const Data &data);

    typedef add2Base<testStruct, testHandler> addTest2Base;
}

foo A;
A.addTest2Base(data);

Как это правильно реализовать?

★★★★★

Ответ на: комментарий от deterok

https://en.wikipedia.org/wiki/C++11#Alias_templates

// 0-parametric new type alias
typedef OldType<...> NewType;

// *-parametric new type alias
template <...>
using NewType = OldType<...>;

не случайно _type_def, то есть работают они для типов — тут не к месту.

В данном случае нужен псевдоним для функции (то есть значения, а не типа) — в большинстве языков, безотносительно происхождения, новые функции определяют на основе существующих с помощью ... определений функций :)

Плюс тут функцию шаблонная, то есть её по-любому нужно специализировать где-то в коде (читай, в коде какой-то _функции_) чтобы компилятор сгенерировал версию для конкретных типов.

quasimoto ★★★★ ()
Ответ на: комментарий от deterok

В Си не было таких костылей! Это каноничный плюсовый костыль. Да и не костыль даже.

yoghurt ★★★★★ ()
Ответ на: комментарий от quasimoto

безотносительно происхождения

Например

-- maxBound :: Bounded a => a
-- 
-- template <typename A> A maxBound();
-- template <> char maxBound() { return ... };

maxBoundForChar :: Char
maxBoundForChar = maxBound :: Char -- again
-- 
-- char maxBoundForChar() { return maxBound<char>(); }
quasimoto ★★★★ ()

Что add2Base() делает с шаблонными типами? Что вообще послужило причиной использовать шаблоны? Нельзя ли здесь обойтись обычной перегрузкой методов?

nanoolinux ★★★★ ()
Ответ на: комментарий от quasimoto

Я прекрасно понимаю, что 11 стандарт уже плотно входит в нашу жизнь, но в данном случае мне нужно сделать все в рамках 3 года...
Думаю воспользоваться вашим первым советом

void addTest2Base(const Data &data) {
        return add2Base<testStruct, testHandler>(data);
    }
Спасибо за помощь.

Кстати, стоит ли это запихнуть в макрос?
Таких штук 20 будет.

deterok ★★★★★ ()
Последнее исправление: deterok (всего исправлений: 1)

struct foo {
   template<class Struct, class Handler>
   struct Add2Base {
      void operator() (Data const& data);
   };

   template<class Struct, class Handler>
   void add2Base(Data const& data) {
      return Add2Base<Struct, Handler>()(data);
   }

   Add2Base<testStruct, testHandler> addTest2Base;
}

BlackHawk ()

А спецификация шаблона вам не подойдет?

andreyu ★★★★★ ()
Ответ на: комментарий от deterok

нужно сделать все в рамках 3 года

Я и не предлагал 11, наоборот, к тому, что хоть старый typedef, что новый (C++11) using определяют псевдонимы (простые у typedef и параметрические в случае using) для _типов_. А вот чтобы в C++ создать псевдоним для функции или метода (которые _значения_, а не типы) — нужно

1) Написать этот псевдоним как функцию или метод в виде обвёртки, то есть

    void addTest2Base_1(const Data &data) {
        add2Base<testStruct, testHandler>(data); }
    // x20 == 40 LOC

одинаково что для функций, что для методов. Явное указание <testStruct, testHandler> заставляет компилятор написать инстанс шаблона для этих типов — эта функция будет звониться / инлайниться, сама addTest2Base_1, будучи в заголовочном файле, тоже будет инлайниться.

С макросом:

    #define ALIAS_add2Base(NAME, T1, T2) \
        void NAME(const Data &data) { add2Base<T1, T2>(data); }

    ALIAS_add2Base(addTest2Base_2, testStruct, testHandler)
    // x20 == 20 + 3 LOC

в зависимости от того насколько неохота писать каждый раз сигнатуру и вызов старой функции.

2) Либо сделать глобальный (наполнять класс лишними полями — не вариант) константный указатель или ссылку на функцию или метод. Если метод — только указатель, так как ссылок на методы в C++ нет:

void(Foo::*const addTest2Base_3)(const Foo::Data &data) =
    &Foo::add2Base<Foo::testStruct, Foo::testHandler>;

тут add2Base<Foo::testStruct, Foo::testHandler> тоже провоцирует инстанцирование шаблона для нужных типов, addTest2Base_3 константно (так что вызов через указатель не будет происходить) указывает на эту сгенерированную функцию. В C++11 немного проще:

constexpr auto addTest2Base_4 = &Foo::add2Base<Foo::testStruct, Foo::testHandler>;

В случае методов способ вызова отличается:

    Foo obj(...);
    obj.addTest2Base_1(...);
    obj.addTest2Base_2(...);
    (obj.*addTest2Base_3)(...);
    (obj.*addTest2Base_4)(...);

Ещё у (2) проблемы с перегрузкой — если только

namespace FooAPI {

constexpr auto addTest2Base = &Foo::add2Base<Foo::testStruct, Foo::testHandler>;

}

// (obj.*FooAPI::addTest2Base)(...);

В плане выхлопа компилятора — примерно одно и то же.

quasimoto ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.