Область видимости C++11 и время жизни временной привязки к (const) ссылке (GCC)
У меня есть следующие вопросы, связанные с той же ситуацией (не в целом):
- Почему компилятор не выдает предупреждение, когда временный объект связан со ссылкой?
- Как продлевает жизнь временную работу (когда она привязана к ссылке)?
- Как интерпретировать / понять стандарт C++ (C++11)?
- Это ошибка (в компиляторе)? Должно ли это быть?
Итак, вот о чем мы говорим:
struct TestRefInt {
TestRefInt(const int& a) : a_(a) {};
void DoSomething() { cout << "int:" << a_ << endl; }
protected:
const int& a_;
};
Должен TestRefInt tfi(55);
а также tfi.DoSomething();
Работа? Как и где? Так вот код
TestRefInt tfi(55);
int main() {
TestRefInt ltfi(8);
tfi.DoSomething();
ltfi.DoSomething();
return 0;
}
Что это должно сделать?
Здесь я разработаю еще немного.
Рассмотрим этот реальный (упрощенный) пример. Как это выглядит для начинающего программиста C++? Имеет ли это смысл?
(вы можете пропустить код, проблема та же, что и выше)
#include <iostream>
using namespace std;
class TestPin //: private NonCopyable
{
public:
constexpr TestPin(int pin) : pin_(pin) {}
inline void Flip() const {
cout << " I'm flipping out man : " << pin_ << endl;
}
protected:
const int pin_;
};
class TestRef {
public:
TestRef(const TestPin& a) : a_(a) {};
void DoSomething() { a_.Flip(); }
protected:
const TestPin& a_;
};
TestRef g_tf(1);
int main() {
TestRef tf(2);
g_tf.DoSomething();
tf.DoSomething();
return 0;
}
Командная строка:
/** Compile:
Info: Internal Builder is used for build
g++ -std=c++11 -O0 -g3 -Wall -Wextra -Wconversion -c -fmessage-length=0 -o "src\\Scoping.o" "..\\src\\Scoping.cpp"
g++ -o Scoping.exe "src\\Scoping.o"
13:21:39 Build Finished (took 346ms)
*/
Выход:
/**
I'm flipping out man : 2293248
I'm flipping out man : 2
*/
Проблема:TestRef g_tf(1); /// shouldn't the reference to temporary extend it's life in
глобальный охват тоже?
Что говорит стандарт?
От того, как auto&& продлит срок службы временного объекта?
Обычно временный объект длится только до конца полного выражения, в котором он появляется. Однако C++ намеренно указывает, что связывание временного объекта со ссылкой на const в стеке удлиняет время жизни временного объекта до времени жизни самой ссылки.
^^^^^
Глобалы не размещаются в стеке, поэтому время жизни не увеличивается. Однако компилятор не выдает никаких предупреждений! Не должно ли это по крайней мере сделать это?
Но моя главная мысль: с точки зрения удобства использования (имеется в виду, как пользователь C++, GCC как программист) было бы полезно, если бы тот же принцип был распространен не только на стек, но и на глобальную область, продлевая время жизни (делая глобальный временные "постоянные").
Sidenote: Проблема еще более осложняется тем, что TestRef g_tf(1);
действительно TestRef g_tf{ TestPin{1} };
но временный объект не виден в коде, и, не глядя на объявление конструктора, он выглядит так, будто конструктор вызывается целым числом, и это редко приводит к возникновению такого рода ошибок.
Насколько я знаю, единственный способ решить эту проблему - запретить временные файлы при инициализации, удалив TestRefInt(const int&& a) =delete;
конструктор. Но это также запрещает это в стеке, где продление жизни работало.
Но предыдущая цитата не совсем соответствует стандарту C++ 11. Соответствующие части стандарта C++ 11 - 12.2 p4 и p5:
4. Существуют два контекста, в которых временные уничтожаются в другой точке, чем конец полного выражения. Первый контекст [...]
5 - Второй контекст, когда ссылка связана с временным. Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом подобъекта, к которому привязана ссылка, сохраняется в течение всего времени существования ссылки, за исключением:
- Временная привязка к элементу ссылки в ctor-initializer конструктора (12.6.2) сохраняется до выхода из конструктора.
- временная граница с опорным параметром в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов. § 12,2 245 с ИСО / МЭК N3337
- Срок действия временной привязки к возвращенному значению в операторе возврата функции (6.6.3) не продлевается; временное уничтожается в конце полного выражения в операторе возврата.
- Временная привязка к ссылке в новом инициализаторе (5.3.4) сохраняется до завершения полного выражения, содержащего новый инициализатор. [Пример: struct S { int mi; const std::pair& mp; }; S a { 1, {2,3} }; S* p = новый S{ 1, {2,3} }; // Создает висячую ссылку - конец примера] [Примечание: это может привести к появлению висячей ссылки, и реализации рекомендуется выдавать предупреждение в таком случае. - конец примечания]
Мой английский и понимание стандарта недостаточно хороши, так что же это за исключение? "- Временная привязка к элементу ссылки в ctor-initializer конструктора (12.6.2) сохраняется до выхода из конструктора". Или "параметр ссылки в вызове функции" не упоминает ничего о распределении в стеке или в глобальной области видимости. Другие исключения, похоже, не применяются. Я ошибся?
Кто это, или никто из них?
У нас есть временный вызов функции (конструктор), а затем ссылка на временный объект привязывается к ссылке на член в инициализаторе. Это неопределенное поведение? Или исключение все еще применяется, а временное должно быть уничтожено? (или оба)
Как насчет этого?
struct TestRefIntDirect {
TestRefIntDirect(int a) : a_(a) {};
void DoSomething() { cout << "int:" << a_ << endl; }
protected:
const int& a_;
};
Еще одна ссылка, такое же поведение.
Почему это работает в одном случае (создание экземпляра внутри функции) по сравнению с другим случаем (в глобальной области видимости)?
Разве GCC не "уничтожает" одного из них "случайно"?
Насколько я понимаю, ни один из них не должен работать, временный не должен сохраняться, как говорит стандарт. Кажется, что GCC просто "позволяет" вам получить доступ к не сохраняющимся объектам (иногда). Я предполагаю, что стандарт не определяет, о чем должен предупреждать компилятор, но можем ли мы согласиться с этим? (в других случаях он предупреждает о "возврате ссылки на временный объект"). Я думаю, что и здесь.
Это ошибка или, может быть, где-то должен быть запрос функции?
Мне кажется, что GCC говорит: "Ну, ты не должен трогать это печенье, но я оставлю его здесь для тебя и не буду никого предупреждать об этом".… Что мне не нравится.
Эту вещь другие ссылки на стек я не нашел в стандарте, откуда она взялась? Это дезинформация о стандарте? Или это следствие чего-то в стандарте? (Или это просто совпадение реализации, что временный объект не перезаписан и на него можно ссылаться, потому что он оказался в стеке? Или это поведение, определяемое компилятором, расширение?)
Чем больше я знаю C++, тем больше кажется, что каждый день он вручает мне пистолет, чтобы выстрелить себе в ногу с ним…
Я хотел бы получить предупреждение от компилятора, если я делаю что-то не так, чтобы я мог это исправить. Неужели я прошу так много?
Другие связанные посты:
C++: постоянная ссылка на временный
Продлевает ли константная ссылка временную жизнь?
Возврат временного объекта и привязка к константной ссылке
Я не хотел так много писать. Если вы все прочитали, спасибо.