我有以下状态机示例,它使用枚举来专门化带有可变参数包的模板方法。当我通过引用传递属性 (Properties&) 而不是通过值传递属性 (Properties) 时,为什么不调用第二个专业化?
enum class State
{
StateA,
StateB
};
enum class Event
{
Event1,
Event2
};
struct Properties
{};
template <typename TState, typename TEvent>
struct StateMachine
{
template <TState tstate, TEvent tevent, typename ...TArgs>
void enter(TArgs ...args)
{
std::cout << "Default" << std::endl;
}
};
template <>
template <>
void
StateMachine<State, Event>::enter<State::StateA, Event::Event1>(int i)
{
std::cout << "Specialized 1" << std::endl;
}
template <>
template <>
void
StateMachine<State, Event>::enter<State::StateA, Event::Event2>(Properties& properties)
{
std::cout << "Specialized 2" << std::endl;
}
int main(int argc, char* argv[])
{
StateMachine<State, Event> sm;
int value = 123;
sm.enter<State::StateA, Event::Event1>(value);
Properties properties;
sm.enter<State::StateA, Event::Event2>(properties);
}
我期待输出:
Specialized 1
Specialized 2
但得到:
Specialized 1
Default
当我按值传递属性时,它可以正常工作。这是完美转发问题吗?我该如何解决?
问题在于,只有通用版本(也称为主模板)参与重载,并且在
enter
的主模板中,您的参数由值获取,而在属性的专用模板中,参数由左值获取参考。
如何修复?
这意味着有有两种方法可以解决这个问题。您可以使用
args
左值引用或显式传递 Properties&
作为第三个参数,如下所示。
这里我们通过将
args
更改为 TArgs ...args
来使 TArgs& ...args
成为非常量的左值引用。请注意,您还可以将其作为 const 的左值引用,因为 enter
不会更改任何内部结构。
template <typename TState, typename TEvent>
struct StateMachine
{
template <TState tstate, TEvent tevent, typename ...TArgs>
//------------------v-------------------->added lvalue ref
void enter(TArgs& ...args)
{
std::cout << "Default" << std::endl;
}
};
template <>
template <>
void
//-----------------------------------------------------------------v-->lvalue ref
StateMachine<State, Event>::enter<State::StateA, Event::Event1>(int& i)
{
std::cout << "Specialized 1" << std::endl;
}
int main(int argc, char* argv[])
{
StateMachine<State, Event> sm;
Properties properties;
sm.enter<State::StateA, Event::Event2>(properties); //prints specialized 2
}
第二种方式是显式传递第三个参数
Properties&
。
int main(int argc, char* argv[])
{
StateMachine<State, Event> sm;
Properties properties;
//-----------------------------------------vvvvvvvvvvv--->explicitly pass third argument
sm.enter<State::StateA, Event::Event2, Properties&>(properties); //prints specialized 2
}