首页 > 解决方案 > 在完美转发函数中公开参数类型,避免代码重复

问题描述

我有一个烦人的场景,我需要推迟一些对象的初始化state并允许用户按需构造一个。例如

// user code

context c;
// ...do something...
c.initialize_state(a, b, c);

// library code

class context
{
private:
    class state
    {
        state(A a, B b, C c);

        state(const state&) = delete;
        state(state&&) = delete;
    };

    std::optional<state> _state; // or `boost::optional`

public:
    template <typename... Xs>
    void initialize_state(Xs&&... xs) 
    {
        _state.emplace(std::forward<Xs>(xs)...);
    }
};

从上面的代码可以看出, 的界面并没有context::initialize_state告诉用户如何初始化。用户被迫查看的实现,然后查看以了解应该传递给的内容。context::_stateinitialize_statestate::stateinitialize_state

我可以换成initialize_state...

void initialize_state(A&& a, B&& b, C&& c) 
{
    _state.emplace(std::move(a), std::move(b), std::move(c));
}

...但这有一个主要缺点:存在代码重复state::state,需要手动维护,以防参数类型发生变化。

有什么方法可以让我两全其美(干燥和用户友好的界面)?请注意,这state是不可移动/可复制的。

标签: c++c++11variadic-templatesdryperfect-forwarding

解决方案


该类state可能不可复制/可移动,但似乎A,BC是。(所以我假设还有一些其他的内部数据state阻止了可复制性/可移动性)

您可以将这些成员拉出到另一个可以注入的类中state。由于缺乏更好的名称,我将其命名为state_args

struct state_args
{
   explicit state_args(A a, B b, C c);
   A a_;
   B b_;
   C c_;
};

这启用了以下功能:

class context
{
private:
    class state
    {
        state(state_args args);

        state(const state&) = delete;
        state(state&&) = delete;
    };

    std::optional<state> _state; // or `boost::optional`

public:
    template<class STATE_ARGS, /*enable_if to ensure STATE_ARGS is indeed state_args*/>
    void initialize_state(STATE_ARGS&& internal_state) 
    {
        _state.emplace(std::forward<STATE_ARGS>(internal_state));
    }
};

推荐阅读