1

现在 GCC 4.8.1 和 Clang 2.9 及更高版本支持它们,引用限定符(也称为“*this 的右值引用”)变得更加广泛可用。它们允许类的行为更像内置类型,例如,不允许分配给右值(否则会导致不需要的右值强制转换为左值):

class A
{
    // ...

public:
    A& operator=(A const& o) &
    {
        // ...
        return *this;
    }
};

通常,调用const右值的成员函数是明智的,因此左值引用限定符将不合适(除非右值限定符可用于优化,例如将成员移出类而不是返回副本)。

另一方面,可变运算符(例如预减/增量运算符)应该是左值限定的,因为它们通常返回对对象的左值引用。const因此还有一个问题:除了概念上const的方法之外,是否有任何理由允许在右值引用上调用变异/非方法(包括运算符),const因为 const 正确性(包括mutable在使用内部缓存时的正确应用) ,其中可能包括现在确保某些线程安全保证)在代码库中被忽略了?

澄清一下,我并不是建议在语言级别禁止对右值进行变异方法(至少这可能会破坏遗留代码),但我相信默认(作为一种习惯用法/编码风格)只允许左值用于变异方法通常会导致更清洁、更安全的 API。但是,我对不这样做会导致更清洁、更不令人惊讶的API 的示例感兴趣。

4

4 回答 4

3

如果 R 值用于完成某些任务,则对 R 值进行操作的 mutator 可能很有用,但在此期间它会保持某种状态。例如:

struct StringFormatter {
     StringFormatter &addString(string const &) &;
     StringFormatter &&addString(string const &) &&;
     StringFormatter &addNumber(int) &;
     StringFormatter &&addNumber(int) &&;
     string finish() &;
     string finish() &&;
};
int main() {
    string message = StringFormatter()
            .addString("The answer is: ")
            .addNumber(42)
            .finish();
    cout << message << endl;
}

通过允许 L 值或 R 值,可以构造一个对象,将其传递给一些 mutators,并使用表达式的结果来完成某些任务,而不必将其存储在 L 值中,即使mutators 是成员函数。

另请注意,并非所有变异运算符都返回对自身的引用。用户定义的变异器可以实现他们需要或想要的任何签名。mutator 可能会消耗对象的状态以返回更有用的东西,并且通过作用于 R 值,对象被消耗的事实不是问题,因为否则状态会被丢弃。事实上,消耗对象状态以产生其他有用的东西的成员函数必须被标记为这样,以便更容易看到何时消耗左值。例如:

MagicBuilder mbuilder("foo", "bar");

// Shouldn't compile (because it silently consumes mbuilder's state):
// MagicThing thing = mbuilder.construct();

// Good (the consumption of mbuilder is explicit):
MagicThing thing = move(mbuilder).construct();
于 2013-06-06T22:26:39.040 回答
0

我认为它发生在检索某些值的唯一方法是改变另一个值的情况下。例如,迭代器不提供“+1”或“next”方法。所以假设我正在为 stl 列表迭代器构建一个包装器(也许为我自己的列表支持数据结构创建一个迭代器):

class my_iter{
private:
    std::list::iterator<T> iter;
    void assign_to_next(std::list::iterator<T>&& rhs) {
        iter = std::move(++rhs);
    }
};

在这里,assign_to_next 方法接受一个迭代器,并将这个迭代器分配到该迭代器之后的下一个位置。不难想象这可能有用的情况,但更重要的是,这种实现并不令人惊讶。诚然,我们也可以说iter = std::move(rhs); ++iter;or ++(iter = std::move(rhs));,但我没有看到任何论据来说明为什么它们会更干净或更快。我认为这种实现对我来说是最自然的。

于 2013-12-22T19:31:46.063 回答
0

FWIW HIC++ 同意你的赋值运算符:

http://www.codingstandard.com/rule/12-5-7-declare-assignment-operators-with-the-ref-qualifier/


非常量方法是否应该应用于右值?

这个问题让我很困惑。对我来说,一个更明智的问题是:

const 方法是否应该只适用于右值?

我相信答案是否定的。我无法想象您想要重载 const rvalue*this的情况,就像我无法想象您想要重载 const rvalue 参数的情况一样。

你重载了右值,因为当你知道你可以窃取它们的胆量但你不能窃取一个const对象的胆量时,可以更有效地处理它们。

有四种可能的重载方式*this

struct foo {
    void bar() &;
    void bar() &&;
    void bar() const &;
    void bar() const &&;
};

后两个重载的常量意味着任何一个都不能改变,因此允许重载做什么和允许重载做*this什么之间没有区别。在没有重载的情况下,无论如何都会绑定到左值和右值。const &*thisconst &&*thisconst &&const &

鉴于重载 onconst &&是无用的,并且只是为了完整性而真正提供(证明我错了!),我们只剩下一个用于 ref-qualifiers 的用例:在 non-const rvalue 上重载*this&&可以为重载定义函数体,也可以定义= delete(如果只提供&重载,这会隐式发生)。我可以想象在很多情况下定义一个&&函数体可能是有用的。

通过重载operator->和一元实现指针语义的代理对象operator*,例如boost::detail::operator_arrow_dispatch,可能会发现在其 上使用 ref-qualifiers 很有用operator*

template <typename T>
struct proxy {
    proxy(T obj) : m_obj(std::move(obj)) {}
    T const* operator->() const { return &m_obj; }
    T operator*() const& { return m_obj; }
    T operator*() && { return std::move(m_obj); }
private:
    T m_obj;
};

如果*this是右值,则operator*可以通过移动而不是复制返回。

于 2014-06-29T17:58:09.703 回答
-1

我可以想象从实际对象移动到参数的函数。

于 2013-06-06T22:18:58.723 回答