7

我有一个'记住'一个对象的引用(例如整型变量)。我不能让它引用一个立即遭到破坏的价值,我正在寻找一种方法来保护我的班级的用户不会意外地这样做。防止传递临时对象

rvalue-reference overload是一种防止临时传入的好方法吗?

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 

} 

一个私人的rvalue重载吗?

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

临时不结合左值参考。在第一个例子中定义'r2'不应该编译。 – musiphil

+0

如果您使用的是VC++,一种解决方案是打开警告级别,它会告诉您什么时候它不起作用。 –

+4

但是,_const_引用会绑定到一个临时的,所以这个问题仍然是一个很好的问题。我已经考虑过这种方法,但我仍然认为,如果一个类将存储引用对象的引用(或指针),最好在构造函数中使用指针,以使潜在的生命期关注点有点更明显(当一个构造函数接受一个指针时,通常它会让我三次思考对象将如何处理它)。 –

回答

2

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

+0

谢谢;似乎我必须更好地审视我的代码并重新解释问题。 – xtofl

3

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

在C++ 11我宁愿删除功能私有函数。它有点更明确,你真的不能称之为(即使你是该类的成员或朋友,也不是。)

N.B.如果被删除的构造是HasRef(int&&)=delete它不会在这里选择:

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

随着类型const int&&HasRef(const int&)构造函数的参数将被使用,而不是HasRef(int&&)之一。在这种情况下,这将是确定的,因为i确实是一个左值,但普遍认为可能并非如此,所以这可能是非常罕见的时代之一,当一个常量右值引用是有用的:

HasRef(const int&&) = delete; 
0

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

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

+0

说起来容易做起来难。使用MSVC禁用语言扩展使他们自己的标准库抱怨。 –

+0

@AlexandreC。到目前为止,我没有遇到任何问题......幸运? –

+0

也许吧。我记得MSVC2005有问题。 –

2

如果你有一个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; },虽然它造成类似的警告:

警告:在返回类型“常量”类型限定符没有效果[-Wignored-预选赛]

还算可以,如果你有效果只是= 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版本所有构造的combinatorically可能版本如下:

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{}}; 
+0

好点,多参数构造函数!可能将所有参数设置为模板是有意义的,并且静态地声明所有参数都是左值。这将防止混合爆炸。 – xtofl

+0

@xtofl简单的'static_assert'不是一个好的选择,当你将一个类型trait像'std :: is_constructible'应用到'A'时。您需要SFINAE-out不好的组合,或者如果可用,则需要使用* Concept Lite *。 – Orient