7

У меня есть класс, который «запоминает» ссылку на какой-либо объект (например, целочисленную переменную). Я не могу ссылаться на значение, которое немедленно разрушено, и я ищу способ защитить пользователей моего класса от случайного выполнения.предотвратить пропуски для временного объекта

Является ли rvalue-reference перегрузкой хорошим способом предотвратить временное перемещение?

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 действительно именующий, но в целом, что может быть не так, так что это может быть один из очень редких случаев, когда Const Rvalue ссылка полезна:

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; все перегрузки конструктора, которые могут принимать значения r (как 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 &&` 
}; 

Этот подход позволяет запретить передачу конструкторам всех видов rvalues.

Это также работает со встроенными скалярами.Например, double const f() { return 0.01; }, хотя это вызывает предупреждение вроде:

предупреждения: «Const» квалификатор типа на тип возвращаемого значения не имеет никакого эффекта [-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 версии для всех в комбинаторно возможные варианты конструкторов следующим образом:

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

Хорошая точка, конструкторы multi arg! Вероятно, было бы разумно сделать все шаблоны шаблонами и статически утверждать, что все ссылки на lvalue каким-то образом. Это предотвратит взрыв смеси. – xtofl

+0

@xtofl Простой 'static_assert' не является хорошим выбором, когда вы применяете свойство типа' std :: is_constructible' к 'A'. Вам нужны SFINAE-из плохих комбинаций или использовать * Concept Lite *, если он доступен. – Orient