专门化可变参数模板成员函数时出现的问题

问题描述 投票:0回答:1

我有以下状态机示例,它使用枚举来专门化带有可变参数包的模板方法。当我通过引用传递属性 (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

当我按值传递属性时,它可以正常工作。这是完美转发问题吗?我该如何解决?

c++ variadic-templates template-meta-programming
1个回答
0
投票

问题在于,只有通用版本(也称为主模板)参与重载,并且在

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
}

演示


方法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
}

演示

© www.soinside.com 2019 - 2024. All rights reserved.