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

Problems when specializing variadic template member function

提问人:pic32cpp 提问时间:10/28/2023 最后编辑:pic32cpp 更新时间:10/28/2023 访问量:68

问:

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

评论

0赞 songyuanyao 10/28/2023
您也可以将第三个模板参数指定为 .sm.enter<State::StateA, Event::Event2, Properties&>(properties);
0赞 Igor Tandetnik 10/28/2023
我的猜测是,在专业化中被推导为 ,但在调用中被推导为TArgs...Properties&Properties
0赞 pic32cpp 10/28/2023
咄!谢谢,修好了。

答:

3赞 user12002570 10/28/2023 #1

问题在于,只有通用版本(又名主模板)参与重载,并且在主模板中,参数由值获取,而在属性的专用模板中,参数由左值引用获取。也就是说,在主模板中,第三个参数被推导出为 while 对于专业化,它是 。enterPropertiesProperties&

如何解决?

这意味着有两种方法可以解决这个问题。您可以进行左值引用,也可以显式传递为第三个参数,如下所示。argsProperties&

方法 1

在这里,我们通过将 更改为 来对 non-const 进行左值引用。请注意,您也可以将其作为对 const 的左值引用,因为不会更改任何内部结构。argsTArgs ...argsTArgs& ...argsenter

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
}

演示