Функция отправки C ++ с параметрами шаблона

Я нахожусь в процессе рефакторинга большого класса — давайте назовем это Big — у этого есть огромное количество кода копирования-вставки. Большая часть этого кода копирования-вставки существует в switch caseгде только участвующие типы оказываются разными. Код переключается на основе enum переменная-член класса, значение которого известно только во время выполнения.

Моя попытка исправить это включает в себя наличие Dispatcher класс, который ищет соответственно типизированные функции через static функция называется lookup(), Функции, которые выполняют реальную работу, всегда называются go() и должны быть определены в шаблоне класса-оболочки (единственным параметром которого является среда выполнения enum значение в настоящее время включается). go() функции могут быть или не быть самими шаблонными функциями.

Вот дистиллированная версия кода. Мои извинения за длину, но это было настолько коротким, насколько я мог получить это без потери важного контекста.

#include <cassert>

class Big
{
public:

enum RuntimeValue { a, b };

Big(RuntimeValue rv) : _rv(rv) { }

bool equals(int i1, int i2)
{
return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2);
}

template<typename T>
bool isConvertibleTo(int i)
{
return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i);
}

private:

template<RuntimeValue RV>
struct Equals
{
static bool go(int i1, int i2)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return i1 == i2;
}
};

template<RuntimeValue RV>
struct IsConvertibleTo
{
template<typename T>
static bool go(int i)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return static_cast<T>(i) == i;
}
};

template<template<RuntimeValue> class FunctionWrapper, typename Function>
struct Dispatcher
{
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go;
case b: return &FunctionWrapper<b>::go;

default: assert(false); return 0;
}
}

template<typename T>
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go<T>;
case b: return &FunctionWrapper<b>::go<T>;

default: assert(false); return 0;
}
}

// And so on as needed...
template<typename T1, typename T2>
static Function * lookup(RuntimeValue rv);
};

RuntimeValue _rv;
};

int main()
{
Big big(Big::a);

assert(big.equals(3, 3));
assert(big.isConvertibleTo<char>(123));
}

Это в основном работает, за исключением того, что:

  1. Он прекрасно работает и работает в Visual C ++ 9 (2008), но в GCC 4.8 это приводит к ошибкам компиляции при перегрузке функции-шаблона lookup(),
  2. Требуется, чтобы новая функция-шаблон перегружалась lookup() записываться для каждого нового числа параметров шаблонов функций, которые мы хотим поддерживать в go(),
  3. Это громоздко и запутанно в использовании.

Вот ошибки, которые происходят в GCC:

Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
Big.cpp(66,65) : error: expected primary-expression before '>' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(66,66) : error: expected primary-expression before ';' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(67,65) : error: expected primary-expression before '>' token
case b: return &FunctionWrapper<b>::go<T>;
^
Big.cpp(67,66) : error: expected primary-expression before ';' token
case b: return &FunctionWrapper<b>::go<T>;
^

Мой вопрос двоякий:

  1. Почему это не удается собрать в GCC, и как мне это исправить?
  2. Есть ли лучший (то есть, менее громоздкий и запутанный) способ сделать это?

Код должен быть компилируемым в Visual C ++ 9 (2008), поэтому я не могу использовать ничего специфичного для C ++ 11.

5

Решение

поскольку go является зависимым именем шаблона, вам нужно использовать template disambiguator:

case a: return &FunctionWrapper<a>::template go<T>;
//                                  ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
//                                  ^^^^^^^^

Это говорит компилятору проанализировать, что следует за оператором разрешения контекста (::) как имя шаблона и последующие угловые скобки в качестве разделителей для аргументов шаблона.

Почему это не удается собрать в GCC, и как мне это исправить?

Потому что GCC соответствует стандарту и выполняет поиск по двухфазному имени, в то время как MSVC задерживает поиск имени до времени создания экземпляра и, следовательно, знает, что go это имя шаблона.

До создания экземпляра эта информация недоступна, потому что невозможно знать, что T есть, и основной шаблон может быть специализированным для данного T чтобы go это не имя шаблона функции-члена, а скорее элемент данных.

При этом я ожидаю, что MSVC поддержит template в любом случае, устранение неоднозначности, поэтому добавление его должно заставить вашу программу компилироваться как в GCC / Clang / независимо от соответствия стандарту, так и в MSVC.

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]