重载分辨率和模板功能

时间:2013-01-28 14:52:23

标签: c++ templates overloading specialization

我有一个带有两个不同功能的模板类(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)...);
}

如果这是不好的做法,我可以就设计获得一些建议吗?

1 个答案:

答案 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 articlethis presentation。棘手的部分是&&后缀并不总是意味着右值引用:例如,它不是主模板的情况,但是在专用模板的情况下则不然。

因此,如果函数的输入参数是左值(例如,带有名称的变量而不是函数调用的返回值),由于常规的重载决策规则,它们将绑定到主模板而不是专用模板