10

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

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

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?

有没有我没看到的陷阱?

4

4 回答 4

7

如果您必须将某个const类型实例的引用存储B到您的类A中,那么您肯定希望确保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' 类型限定符无效 [-Wignored-qualifiers]

如果您只是构造函数的版本,它仍然可以= 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{}};
于 2016-12-23T13:06:39.533 回答
3

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

在 C++11 中,我更喜欢删除函数而不是私有函数。更明确一点,你真的不能调用它(即使你是班级的成员或朋友也不能。)

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

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

对于类型的参数,const int&&HasRef(const int&)使用构造函数,而不是那个HasRef(int&&)。在这种情况下没问题,因为i真的是一个左值,但通常情况可能并非如此,所以这可能是const右值引用有用的极少数情况之一:

HasRef(const int&&) = delete;
于 2012-06-20T20:47:03.880 回答
2

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

于 2012-06-20T20:42:16.150 回答
0

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

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

于 2012-06-20T20:56:28.327 回答