2013-01-28 7 views
2

У меня есть шаблон класса (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 должен быть усеян всеми этими различными версиями функции подготовки. И я не чувствовал, что подклассификация подходит, потому что различия между различными OutgoingPackets будут очень маленькими (~ 4 строки).

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

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

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

ответ

3

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

Пожалуйста, обратите внимание, что параметры функции вашего основного VARIADIC шаблон связывается с обеими rvalues ​​и lvalues: другими словами, они являются универсальными ссылками (нестандартный термин Скотта Мейерс), в то время как параметры функции в вашем специализированном привязка шаблона только к rvalues.

// 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); 

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

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

+0

Кажется, что я не использовал rvalues. Я получаю ошибки времени компиляции, когда я пытаюсь, больше проблем для разработки, я полагаю. – vmrob

Смежные вопросы