防止临时对象的传递引用

prevent pass-by-ref of temporary object

本文关键字:引用 临时对象      更新时间:2023-10-16

我有一个类,它"记住"对某个对象的引用(例如整数变量)。 我不能让它引用立即销毁的值,我正在寻找一种方法来保护我的类的用户不会意外这样做。

右值引用重载是防止传入临时的好方法吗?

struct HasRef {
    int& a;
    HasRef(int& a):a(a){}
    void foo(){ a=1; }
};

int main(){
    int x=5;
    HasRef r1(x);
    r1.foo();  // works like intended.
    HasRef r2(x+4);
    r2.foo(); // dereferences the temporary created by x+4
 }

私人价值过载会吗?

 struct HasRef {
   int& a;
   HasRef( int& a ):a(a){}
   void foo(){ a=1; }
 private: 
   HasRef( int&& a );
 };
 ... HasRef r2(x+1); // doesn't compile => problem solved?
有什么陷阱我没有

看到吗?

如果您必须在类A中存储对某个类型 B 实例的const引用,那么您肯定希望确保A实例的生存期将超过B实例的生存期:

B b{};
A a1{b}; // allowed
A a2{B{}}; // should be denied
B const f() { return B{}; } // const result type may make sense for user-defined types
A a3{f()}; // should also be denied!

为了实现这一点,您应该显式= delete;所有构造函数重载,这些重载可以接受右值(const &&&& )。为此,您应该只= delete; const &&版本的构造函数。

struct B {};
struct A
{
    B const & b;
    A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &`
    A(B const &&) = delete; // prohibits both `B &&` and `B const &&`
};

此方法允许您禁止向构造函数传递各种右值。

这也适用于内置标量。例如,double const f() { return 0.01; } ,尽管它会导致如下警告:

警告:返回类型的"const"类型限定符不起作用 [-Wignore 限定符]

如果您只= delete; &&版本的构造函数,它仍然可以生效:

struct A
{
    double const & eps;
    A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&`
    A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&`
};
double const get_eps() { return 0.01; }
A a{0.01}; // hard error
A a{get_eps()}; // no hard error, but it is wrong!

对于非转换构造函数(即非一元),存在一个问题:您可能必须为所有组合可能的构造函数版本提供 = delete; -d 版本,如下所示:

struct A
{
    A(B const &, C const &) {}
    A(B const &&, C const &&) = delete;
    // and also!
    A(B const &, C const &&) = delete;
    A(B const &&, C const &) = delete;
};

禁止混合情况,例如:

B b{};
A a{b, C{}};
忽略

代码无效的事实,只回答有关私有重载的问题......

在 C++11 中,我更喜欢删除的功能而不是私有功能。更明确的是,你真的不能称呼它(即使你是班级的成员或朋友。

注意:如果删除的构造函数HasRef(int&&)=delete则不会在此处选择:

int i;
HasRef hr(std::forward<const int>(i));

对于类型 const int&& 的参数,将使用 HasRef(const int&) 构造函数,而不是HasRef(int&&)构造函数。在这种情况下,这是可以的,因为i确实是一个左值,但一般来说可能并非如此,所以这可能是常右值引用有用的非常罕见的情况之一:

HasRef(const int&&) = delete;

这不应该编译。一个好的C++编译器(或者我见过的几乎任何C++编译器)将阻止这种情况发生。

我猜你是在MSVS中编译的。在这种情况下,请关闭语言扩展,您应该会收到错误。

否则,即使标记引用const也不会延长临时的生存期,直到构造函数完成。之后,您将引用无效对象。