Разрешение перегрузки и функции шаблона

У меня есть шаблон класса (OutgoingPacket) с двумя различными функциями:

void _prepare() {
assert(false); // this should have been specialized and the native function never called.
}

template <typename Args, typename ... RemArgs>
void _prepare(Args&& args, RemArgs&& ... remArgs) {
assert(false); // this should have been specialized and the native function never called.
}

Затем я определяю некоторые из этих двух специализаций вне определения класса:

// no args
template <> void OutgoingPacket<PacketServerAck> ::_prepare();
template <> void OutgoingPacket<PacketSup>       ::_prepare();
template <> void OutgoingPacket<PacketWelcome>   ::_prepare();
template <> void OutgoingPacket<PacketServerPing>::_prepare();

// with args
template <> template <> void OutgoingPacket<PacketGTFO>::_prepare<std::string>(std::string&& message);
template <> template <> void OutgoingPacket<PacketPlayer>::_prepare<std::shared_ptr<User>>(std::shared_ptr<User>&& user);

Вызовы функций для подготовки без аргументов работают должным образом, но вызовы перегрузки с аргументами вызывают базовый шаблон; они вызывают утверждение.

Почему это происходит?


Обновление: я только что попытался изменить определения специализации, чтобы включить ссылку с тем же результатом:

template <> template <> void OutgoingPacket<PacketGTFO>::_prepare<std::string&&>(std::string&& message);
template <> template <> void OutgoingPacket<PacketPlayer>::_prepare<std::shared_ptr<User>&&>(std::shared_ptr<User>&& user);

Кстати, причина, по которой я так поступаю, заключается в том, что я не чувствовал, что базовый класс OutgoingPacket должен быть завален всеми этими различными версиями функции prepare. И я не чувствовал, что подклассы были бы уместными, потому что различия между различными исходящими пакетами были бы очень маленькими (~ 4 строки).

По сути, объект OutgoingPacket создается с произвольными аргументами, которые затем направляются в функцию prepare:

template<typename ... Args>
OutgoingPacket(Args&&... args) {
_prepare(std::forward<Args>(args)...);
}

Если это плохая практика, могу ли я получить совет по дизайну?

2

Решение

Причина, скорее всего, в том, что вы вызываете свои функции с аргументами, которые не rvalues.

Обратите внимание, что параметры функции вашего основного шаблона переменной связаны как с rvalue, так и с lvalue: другими словами, они универсальные ссылки (нестандартный термин Скотта Мейерса), а параметры функции в вашем специализированном шаблоне связываются только к значениям.

// The parameters are universal references
template <typename Args, typename ... RemArgs>
void _prepare(Args&& args, RemArgs&& ... remArgs)

// The parameters are NOT universal references
template <> template <>
void OutgoingPacket<PacketGTFO>::_prepare<std::string>(std::string&& message);

Увидеть Эта статья а также эта презентация для объяснения того, как работают универсальные ссылки. Сложность в том, что && Суффикс не всегда означает ссылку на значение rvalue: например, в случае вашего основного шаблона это не так, но в случае вашего специализированного шаблона это так.

Таким образом, если входными аргументами вашей функции являются lvalues ​​(например, переменные с именами, а не возвращаемым значением вызова функции), они будут привязаны к вашему первичному шаблону, а не к вашему специализированному шаблону, из-за обычных правил разрешения перегрузки.

3

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

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

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