使用rvalues编写operator +的正确方法

时间:2017-10-19 16:42:30

标签: c++ visual-c++

我一直在编写自己的String类,我不确定如何正确编写operator +,因为我可以将rvalues传递给它。我想我应该有以下3个非成员函数

String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);

但我不确定如何实施它们。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

首先,确保在String类中定义复制和移动构造函数:

class String
{
private:
    char *m_data;
    std::size_t m_length;
    ...

public:
    String();
    String(const String &src);
    String(String &&src);
    ~String();
    ...
};

String::String() :
    m_data(nullptr),
    m_length(0)
{
}

String(const String &src) :
    m_data(new char[src.m_length+1]),
    m_length(src.m_length)
{
    std::copy_n(src.m_data, m_length, m_data);
    m_data[m_length] = 0;
}

String(String &&src) :
    m_data(nullptr),
    m_length(0)
{
    std::swap(m_data, src.m_data);
    std::swap(m_length, src.m_length);
}

String::~String()
{
    delete[] m_data;
}

然后为班级定义operator+operator+=

class String
{
public:
    ...
    String& operator+=(const String &rhs);
    ...
    friend String operator+(String lhs, const String &rhs)
    {
        lhs += rhs;
        return lhs;
    }
};

String& String::operator+=(const String &rhs)
{
    String tmp;
    tmp.m_length = m_length + rhs.m_length;
    tmp.m_data = new char[tmp.m_length+1];
    std:copy_n(m_data, m_length, tmp.m_data);
    std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + m_length);
    tmp.m_data[tmp.m_length] = 0;
    std::swap(m_data, tmp.m_data);
    std::swap(m_length, tmp.m_length);
    return *this;
}

通过在右侧输入const String &作为输入,它将处理左值和左值输入。

对于operator+,左侧是按值获取的,因此编译器可以根据输入是左值(副本)还是右值(移动)来决定使用的最佳构造函数。

或者,您可以实现它以在左侧使用const String &,因此它仍然处理左值和右值,但是您必须实现它,类似于operator+=的实现方式,以避免额外的分配在连接之前复制lhs

friend String operator+(const String &lhs, const String &rhs)
{
    /*
    String tmp(lhs);
    tmp += rhs;
    return tmp;
    */

    String tmp;
    tmp.m_length = lhs.m_length + rhs.m_length;
    tmp.m_data = new char[tmp.m_length+1];
    std:copy_n(lhs.m_data, lhs.m_length, tmp.m_data);
    std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + lhs.m_length);
    tmp.m_data[tmp.m_length] = 0;
    return tmp;
}

无论哪种方式,您还应该为operator+输入定义转换构造函数和const char *

class String
{
public:
    ...
    String(const char *src);
    ...
    friend String operator+(const char *lhs, const String &rhs)
    {
        return String(lhs) + rhs;

        /* or:

        std::size_t len = std::strlen(lhs);
        String tmp;
        tmp.m_length = len + rhs.m_length;
        tmp.m_data = new char[tmp.m_length+1];
        std:copy_n(lhs, len, tmp.m_data);
        std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + len);
        tmp.m_data[tmp.m_length] = 0;
        return tmp;
        */
    }
    ...
};

String::String(const char *src) :
    m_data(nullptr),
    m_length(std::strlen(src))
{
    m_data = new char[m_length+1];
    std::copy_n(src, m_length, m_data);
    m_data[m_length] = 0;
}

这样可以将String个对象与字符串文字(String + "literal""literal" + StringString += "literal"等连接起来。)

有关详细信息,请参阅operator overloading上的cppreference.com

答案 1 :(得分:1)

我通常这样做的方式是这样的:

class foo
{
...
public:
...
  foo&& operator +(foo const & other) &&;
  foo&& operator +(foo && other) const &;
  foo&& operator +(foo && other) &&;
  foo   operator +(foo const & other) const &;
};

不确定Microsoft是否支持此功能,但这是在更新标准中执行此操作的好方法。如果msvc不会让你尝试铿锵。

这样做的好处是,您可以对所使用的方法进行非常精细的控制。如果需要,也可以在类外定义这4个操作。但是对于r值/ l值组合的4种可能性,你总是想要4。

此外,您通常希望将l值限定为const,以表明它们未被修改。

简单地定义复制/移动构造函数通常不是解决此问题的有效方法。您需要很好地理解右值引用如何有效地实现它。