如何在不使用模板的情况下执行完美转发?

时间:2016-04-01 20:34:52

标签: c++ c++11

我想完美转发,但我已经知道(并且只接受)我的功能将采用的类型。

以下是我输入的一个简单示例:

class big_class
{
private:
    std::string m_somethingBig;
};

class testmove
{
public:

    void Add(big_class&& big)
    {
        std::cout << "Add via move\n";
        m_bigClasses.push_back(std::move(big));
    }

    void Add(big_class const& big)
    {
        std::cout << "Add via copy\n";
        m_bigClasses.push_back(big);
    }

private:

    std::vector<big_class> m_bigClasses;
};

int main()
{
    testmove tm;

    big_class big;
    tm.Add(big);

    tm.Add(big_class{});
}

Live Sample

是否可以在testmove::Add()的两个重载之间进行某种形式的实现共享?我想优化移动,如果有人在没有我的右值超载的情况下std::move(),它会在添加到我的向量之前至少做一个副本。

同样,我意识到我可以通过使Add()成为模板函数,甚至使用类型特征和一些模板技巧来解决这个问题。但是如果可能的话我想避免这种情况。如果您需要知道原因,我有几个原因:

  • 我无法使用模板隐藏实现(限制单个翻译单元的包含和符号可见性)
  • 在这里使用模板比我想要的更灵活(我的合同要求我只使用big_class)。
  • 使用模板会影响简单界面的可读性/可维护性。

2 个答案:

答案 0 :(得分:2)

Xeo建议的方法(按参数取值)是我强烈推荐的方法。但是,如果由于某种原因你不能这样做(例如,移动费用昂贵但比副本少),请继续阅读。

我认为可以满足您的所有标准,但如果代码足够复杂以至于重复它会很糟糕,那么它是值得的。我们的想法是委托一个模板,该模板将仅为big_class显式实例化。

big_class.h

// ...
public:
    void Add(big_class&& big)
    {
        Add_internal(std::move(big));
    }
    void Add(big_class const& big)
    {
        Add_internal(big);
    }
private:
    // not part of interface; defined in .cpp file
    template <typename T> void Add_internal(T&& big);

big_class.cpp

// implementation of template
template <typename T> void big_class::Add_internal(T&& big) {
    // shared logic goes here
    m_bigClasses.push_back(std::forward<T>(big));
}
// explicit instantiation
template void big_class::Add_internal<big_class>(big_class&&);
template void big_class::Add_internal<big_class const&>(big_class const&);

答案 1 :(得分:1)

如果您真的无法忍受按值添加的想法,您可以提供一个内部模板化的impl:

private:
    template<class X>
    auto add_impl(X&& x) {
        m_bigClasses.push_back(std::forward<X>(x));
    }

public:

    void Add(big_class&& big)
    {
        std::cout << "Add via move\n";
        add_impl(std::move(big));
    }

    void Add(big_class const& big)
    {
        std::cout << "Add via copy\n";
        add_impl(std::move(big));
    }
  

这怎么办?你正在移动一个常规参考!

因为std::move没有移动任何东西。它只是将一个l值引用转换为r值引用。因此const T&成为const T&&,没有人编码。因此它会衰减以匹配人们为其编码的const T&T