可以做些什么来防止对返回值的误导赋值?
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 一个成员函数来执行您的要求,如下所示。
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;
}
在使用 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 一个成员函数来执行您的要求,如下所示。
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 argumentlvalue 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;
}