我有一个带有两个不同功能的模板类(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函数。我并不认为子类化是合适的,因为不同的OutgoingPackets之间的差异非常小(约4行)。
基本上,OutgoingPacket对象是使用任意参数创建的,然后它会转发到prepare函数:
template<typename ... Args>
OutgoingPacket(Args&&... args) {
_prepare(std::forward<Args>(args)...);
}
如果这是不好的做法,我可以就设计获得一些建议吗?
答案 0 :(得分:3)
原因很可能是您使用不 rvalues的参数调用函数。
请注意,主要可变参数模板的函数参数绑定到rvalues和lvalues:换句话说,它们是通用引用(Scott Meyers的非标准术语),而函数专用模板中的参数仅将 绑定到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。棘手的部分是&&
后缀并不总是意味着右值引用:例如,它不是主模板的情况,但是在专用模板的情况下则不然。
因此,如果函数的输入参数是左值(例如,带有名称的变量而不是函数调用的返回值),由于常规的重载决策规则,它们将绑定到主模板而不是专用模板