2

背景

以下代码块出现在 Scott Meyers 的名著《Effective C++》第 3 条中:

class TextBlock {
public:
    ...
    const char& operator[](std::size_t position) const
    {
        ...    // do bounds checking
        ...    // log access data
        ...    // verify data integrity
        return text[position];
    }
    char& operator[](std::size_t position)
    {
        ...    // do bounds checking
        ...    // log access data
        ...    // verify data integrity
        return text[position];
    }
    ...
private:
    std::string text;
};

作者指出,在上述实现中,const和非const重载的内容本质上是相同的。为了避免代码重复,可以这样简化:

class TextBlock {
public:
    ...
    const char& operator[](std::size_t position) const    // the same as before
    {
        ...
        ...
        ...
        return text[position];
    }
    char& operator[](std::size_t position)        // now just calls const op[]
    {
        return                                    // cast away const on
          const_cast<char&>(                      // op[]'s return type;
            static_cast<const TextBlock&>(*this)  // add const to *this's type;
              [position]                          // call const version of op[]
          );
    }
    ...
private:
    std::string text;
};

问题

我的问题是:

  • 我们什么时候需要一个重载 forconst T&和另一个 for T&&?(这里,T可能是模板参数或类类型,所以T&&可能是也可能不是通用引用)我可以看到在标准库中,许多类都提供了这两种重载。例子是std::pairand的构造函数std::tuple,有大量的重载。(好的,我知道在函数中,一个是复制构造函数,一个是移动构造函数。)

  • 是否有类似的技巧来共享const T&T&&重载的实现?我的意思是,如果const T&&重载返回一个复制构造的对象,而T&&重载返回的是移动构造的对象,那么在共享实现之后,这个属性必须仍然成立。(就像上面的技巧:constreturn constand non- constreturn non- const,在实现共享之前和之后)

谢谢!

澄清

我指的两个重载应该如下所示:

Gadget f(Widget const& w);
Gadget f(Widget&& w);

它与通过右值引用返回无关,即:

Widget&& g(/* ... */);

(顺便说一下,这个问题在我之前的帖子中已经解决了)

f()上面,如果Gadget既可以复制构造又可以移动构造,则没有办法(除了阅读实现)来判断返回值是复制构造还是移动构造。与返回值优化(RVO)/命名返回值优化(NRVO)无关。(见我之前的帖子

参考

有效的 C++

标准::对::对

标准::元组::元组

什么时候是通过右值引用返回的好时机?

4

1 回答 1

2

• 我们什么时候需要一个 const T& 的重载和另一个 T&& 的重载?

基本上,当移动为您带来性能提升时,还应该有一个移动构造函数。对于您原本需要昂贵副本的功能也是如此。

在您的示例中,您返回对 a 的引用char,但是不建议同时设置一个返回右值引用的函数。相反,按值返回并依赖于编译器应用 RVO 的能力(参见例如此处

• 是否有类似的技巧来共享 const T& 和 T&& 重载的实现?

我经常发现使用通用引用(我很懒)设置构造函数或函数很有用,例如

struct MyClass
{
    template<typename T /*, here possibly use SFINAE to allow only for certain types */>
    MyClass(T&& _t) : t(std::forward<T>(_t)) {}
private:
    SomeType t;
};

编辑Widget:关于您的更新:如果您的函数中有昂贵的副本,f建议还提供一个重载以Widget&&.

Gadget f(Widget const& w)
{
    Widget temp = w;  //expensive copy
}
Gadget f(Widget&& w)
{
    Widget temp = std::move(w);  //move
}

您可以使用这样的函数模板组合这两个函数

template<typename WidgetType
       // possibly drop that SFINAE stuff
       // (as it is already checked in the first assignment)
       , typename std::enable_if<std::is_convertible<std::remove_reference_t<WidgetType>, Widget>::value> >
Gadget(WidgetType&& w)
{
    Widget temp = std::forward<WidgetType>(w);
    //or
    std::remove_reference_t<WidgetType> temp2 = std::forward<WidgetType>(w);
}

...我没有说它更好;-)。


编辑 2:另请参阅此线程,它更彻底地解决了您的问题。

于 2015-03-30T12:54:25.933 回答