С++ автоматическое определение аргументов шаблона?
Я пытаюсь определить рекурсивную конструкцию, такую как ферма задач. Здесь я пытаюсь использовать два операнда, которые рекурсивно могут работать для любого количества операндов, поскольку он может сам встраиваться.
template <typename T1, typename T2>
class Farm
{
private:
T1 *task1;
T2 *task2;
public:
// save them so that I can use them when invoking call operator
Farm(T1 *_t1, T2 *_t2): task1(_t1), task2(_t2) { }
void operator()()
{
// invoke call operator, meaning a farm could be a task (arbitrary nesting)
(*task1)();
(*task2)();
}
};
int main()
{
... create two pointer(A *a, B *b...)
Farm(a,b); // error: missing template arguments before ‘(’ token
Farm<A, B>(a,b); // in this works, it works
}
Проблема заключается в автоматическом обнаружении аргументов шаблона, который в этом случае не работает. Что я делаю неправильно, и как я могу достичь этих параметров шаблона, неявное обнаружение gcc-компилятором.
Спасибо!
Ответы
Ответ 1
Классы/конструкторы не автоматически определяют типы, подобные функциям. Вам нужно написать функцию обертки для создания своего класса.
Это делается следующим образом и называется Генератор объектов. (спасибо @Itjax!)
template <typename T1, typename T2>
Farm<T1, T2> makeFarm(T1* a, T2* b) {
return Farm<T1,T2>(a,b);
}
// silly example
Farm<T1,T2> farm = makeFarm(a,b);
// better example
template<typename T>
void plow(T& farm) { farm.applyTractor(...); }
void blah() {
plow(makeFarm(b,a))
}
Этот шаблон возникает довольно часто при использовании lambda/bind/foreach и подобных частей, когда вы хотите создать временный объект шаблонизированного класса с некоторыми аргументами и не указывать их тип, обычно отправляя его в другую функцию шаблона (std::for_each
) или полиморфный объект (std::function
).
Примечание. Функция генератора, как правило, встроенная и с оптимизацией при копировании, вероятно, не будет вообще отсутствовать в вашем коде. Если вы не можете скопировать свой объект, makeFarm() должен возвращать умный указатель вместо этого (std::unique_ptr
является предпочтительным в современном С++).
Ответ 2
Обычным обходным решением является предоставление функции шаблона, которая возвращает реальную реализацию. Стандартная библиотека С++ использует это много, например. с std:: make_pair.
Пример:
template<typename T>
struct foo_t {
...
};
template<typename T>
foo_t<T> foo(T const &f) {
return foo_t<T>(f);
}
Это работает, потому что для функций компилятору разрешено выводить имена типов из списка параметров.
Ответ 3
Вы можете добавить базовый класс для класса Farm:
class FarmBase
{
public:
virtual ~FarmBase(){}
virtual void operator()() = 0;
};
template <typename T1, typename T2>
class Farm : public FramBase
{
private:
T1 *task1;
T2 *task2;
public:
// save them so that I can use them when invoking call operator
Farm(T1 *_t1, T2 *_t2): task1(_t1), task2(_t2) { }
virtual ~Farm(){}
virtual void operator()()
{
// invoke call operator, meaning a farm could be a task (arbitrary nesting)
(*task1)();
(*task2)();
}
};
template< typename A, typename B >
FarmBase* Create( A *a, B *b )
{
return new Farm< A, B >( a, b );
}
то главное выглядит так:
int main()
{
//... create two pointer(A *a, B *b...)
FarmBase *fobj = CreateFarm( a, b );
}