Вопрос о точном времени уничтожения временных файлов в C++

Является ли следующий код безопасным (он работает в DEBUG):

void takesPointer(const Type* v);//this function does read from v, it doesn't alter v in any way
Type getValue();
...
takesPointer(&getValue());//gives warning while compiling "not an lvalue"
...
Type tmp = getValue();
takesPointer(&tmp);//this is safe, and maybe I should just do it, instead of posting here

так - это безопасно? я должен просто забыть об этом и использовать код с явным tmp?

но в любом случае - мне все еще интересно, разрешено ли оптимизатору убивать временное перед возвратом из этого вызова:

takePointer(&getValue())

РЕДАКТИРОВАТЬ: спасибо всем! к сожалению, я не могу изменить функцию "takePointer" (это часть библиотеки), я могу только обернуть ее в функцию "takeReoference", которая вызывает takePointer - это исключит копию, или компилятору все равно будет разрешено создавать копия ("Тип" - это матрица типа int-3x3, так что это было бы НЕ ТАК плохо, но все же...)?

inline void takesReference(const Type& v){ takesPointer(&v); }

О времени уничтожения: будет ли оно уничтожено после ВОЗВРАТА "takePointer" или после того, как оно ВЫЗВАНО?

5 ответов

Как уже говорилось в других ответах, вы не можете получить временный адрес. Однако, если вы измените подпись

void takesPointer(const Type* v);

в

void takesPointer(const Type& v);

тогда следующий код должен компилироваться без предупреждений:

takesPointer(getValue());

потому что вам разрешено привязывать временную ссылку к константной ссылке, и она должна работать точно так же.

Стандарт запрещает вам делать &getValue() - именно потому, что это не lvalue. Обычно, если это было разрешено, то временный результат, вызванный этим вызовом функции, будет действовать до тех пор, пока не вернется внешняя функция, и все остальные элементы во всем выражении не будут обработаны. Это можно назвать "уничтожением временных файлов после завершения полного выражения" и гарантирует, что такие вещи, как следующие, работают, как ожидается

// the temporary string is alive until the whole expression has been processed
cout << string("hello");

Компилятор дает вам диагностику - это все, что требует стандарт для некорректного кода. Например, это не заставляет компилятор прерывать компиляцию. Но после того, как код был неправильно сформирован, компилятор может делать все, что захочет. Поэтому, если вы хотите знать, что делает компилятор в вашем случае, вам следует прочитать его руководство.

Это предотвратит копирование * и компиляцию.

const Type& tmp = getValue(); 
takesPointer(&tmp);

Предотвратить копирование довольно сложно, потому что компилятор часто делает это за вас. У вас должен быть доступ к конструктору копирования, но компилятор часто не использует его:

#include "iostream"
class Type
{
public:
    explicit Type(int val) : m_val(val) { std::cout << "ctor";};
    Type(const Type& copy)
    {
        std::cout << "copy";
    };
private:
    int m_val;
};

Type getValue() { Type r(0); return r;};
void takesPointer(const Type* const)
{

};

int main(int argc, char* argv[])
{
    const Type tmp = getValue(); 
    takesPointer(&tmp);
}

Будет печатать только "ctor" в выпуске и "ctorcopy" в отладке. (MVS2005)

Вам разрешено связывать неконстантное значение с константной ссылкой lvalue, но вы привязываете его к константному указателю lvalue.

И нет, оптимизатор не может разрушить результат getValue() перед звонком takePointer(),

Да, это безопасно, хотя и незаконно в текущей форме. Вы можете обойти ошибку, используя явное промежуточное приведение к типу const-reference

takesPointer( &(const Type &) getValue() );

Это делает его совершенно законным, пока временный объект жив, то есть до конца оценки полного выражения.

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

Используя оператор запятой, вы можете "растянуть" полные выражения и таким образом написать довольно обширные последовательности операций, которые работают с "долгоживущим" временным объектом

Type *p;
p = &(Type &) (const Type &) getValue(), modify(p), print(p), modifyAgain(p), print(p);
// Not using C++ casts for brevity

Практика довольно сомнительна, и в большинстве случаев это бессмысленно.

Другие вопросы по тегам