可以做些什么来防止对返回值的误导赋值?

What can be done to prevent misleading assigment to returned value?

在使用 C++ 多年之后,我意识到在使用自定义 classes 时语法中存在一个怪癖。 尽管是 correct language behavior,但它允许创建非常具有误导性的界面。

此处示例:

class complex_arg {
    double r_;
    double phi_;
 public:
    std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};

int main() {
    complex_arg ca;
    ca.value() = std::complex<double>(1000., 0.);  // accepted by the compiler !?
    assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}

https://godbolt.org/z/Y5Pcjsc8d

可以对 class 定义做些什么来防止这种行为? (或者至少标记第 3 行实际上没有做任何赋值的类的用户。)

我只看到一条出路,但它需要修改 class 并且它不能很好地扩展(到可以移动的大 classes)。

    const std::complex<double> value() const;

我也试过[[nodiscard]] value(),但没用。

作为最后的手段,也许可以对返回的类型做一些事情 std::complex<double> ? (也就是说,假设有人控制 class)


请注意,我了解有时可能需要对新获得的值进行(优化)赋值并将其传递给另一个函数 f( ca.value() = bla )。我并不是在质疑这种用法本身(尽管它也很令人困惑);我的问题主要是 ca.value() = bla; 作为一个独立的语句,它看起来不像它看起来的那样。

通常我们可以调用对象的成员函数,而不管该对象的值类别是左值还是右值。

What can be done to the class definition to prevent this behavior?

在现代 C++ 之前,没有办法阻止这种用法。但自从 C++11 以来,我们可以 ref-qualify 一个成员函数来执行您的要求,如下所示。

来自member functions

During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows:

  • no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument

  • lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X

  • rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X


这使我们能够按照您的要求进行自定义托管 class。特别是,我们可以 & 限定复制赋值运算符。

struct C
{
    C(int)
    {
      std::cout<<"converting ctor called"<<std::endl;
    }
    C(){
        std::cout<<"default ctor called"<<std::endl;
    }
    C(const C&)
    {
     std::cout<<"copy ctor called"<<std::endl;
    }
//-------------------------v------>added this ref-qualifier
    C& operator=(const C&) &
    {
    std::cout<<"copy assignment operator called"<<std::endl;;
    return *this;
    }

};
C func()
{
    C temp;
    return temp;
}

int main()
{
//---------v---------> won't work because assignment operator is & qualified
    func() = 4;
}