Re[10]: Exception safe T Stack::pop() ?
От: N. I.  
Дата: 21.08.18 14:19
Оценка: 10 (1)
uzhas:

U>ты хочешь сказать, что из-за того, что в свежем стандарте появилось больше безопасных вариантов вызова метода T pop() проблема как бы исчезла?

U>она не исчезла, т.к. "опасные" варианты всё еще остались.

"Опасные" варианты использования всегда можно придумать, в том числе и для пары top и pop:

T t = std::move(stack.top());
POTENTIALLY_THROWING_EXPRESSION;
use_value(t);
stack.pop();

Если POTENTIALLY_THROWING_EXPRESSION кинет исключение, то на вершине стека, возможно, останется лежать мусорный объект, а полезное значение топового элемента может быть "утеряно".

U>в стандарте нет таких примеров, что вот вызывайте std::vector::at (для примера) только в одну строчку с присваиванием, иначе мы не даем гарантии на безопасность к исключениям.


Тебе возможные варианты кривого использования std::vector::at привести, что ли?

U>это ерунда какая-то, слишком уж много нюансов надо держать в голове


Мда, это ж какой мегамозг надо иметь, чтоб держать в голове два действия: перемещение/копирование элемента и его удаление... С таким rocket science далеко не каждый справится.

U>что ты скажешь про такие варианты вызова?

U>
void f1(T t);
void f2(const T& t);
void f3(T&& t);

template<typename U>
void f4(U&& t) {}

int main() {
  T t;
  t = stack.pop(); // 1
  f1(stack.pop()); //2
  f2(stack.pop()); //3
  f3(stack.pop()); //4
  f4(stack.pop()); //5
}

U>в каких случаях C++17 нас защищает, а в каких у нас уже будут проблемы при исключениях при копировании?

Если T — это кошерный тип с небросающим перемещающим конструктором и небросающим перемещающим оператором присваивания, то копирования T тут нигде не будет (внутренности f1 — f4 не рассматриваем). В этом случае возможностей C++11 уже достаточно.

В случае, если для возврата значения из pop используется потенциально бросающее копирование, C++17 даёт возможность сделать отложенный вызов деструктора для топового элемента при условии, что исключение брошено не было. Дальше при инициализации параметра t функции f1 C++17 обеспечивает guaranteed copy elision.
Отредактировано 21.08.2018 14:21 N. I. . Предыдущая версия .
Re[9]: Exception safe T Stack::pop() ?
От: Максим Рогожин Россия  
Дата: 21.08.18 15:06
Оценка:
Здравствуйте, N. I., Вы писали:

NI>C++17 в добавок к этому гарантирует, что в следующем случае


NI>
auto t = stack.pop();

NI>переменная t и return object функции pop являются одним и тем же объектом.

Интересно почему это изначально сделано не было?
Re[11]: Exception safe T Stack::pop() ?
От: uzhas Ниоткуда  
Дата: 21.08.18 15:08
Оценка:
Здравствуйте, N. I., Вы писали:

NI>В случае, если для возврата значения из pop используется потенциально бросающее копирование, C++17 даёт возможность сделать отложенный вызов деструктора для топового элемента при условии, что исключение брошено не было.


это стало возможным только благодаря функции std::uncaught_exceptions, ничего другого полезного для данной задачи не появилось в C++17. собственно, об этом я и спрашивал ранее
хотя, стоит заметить, реализация относительно сложная из-за нестандартизованных SCOPE_*, но рабочая
Re[9]: Exception safe T Stack::pop() ?
От: σ  
Дата: 21.08.18 22:12
Оценка:
NI>
T const &t = stack.pop();

NI>- из-за исключения при конструировании временного объекта, который компилятору позволялось создавать в контексте инициализации ссылки t (интересно, когда-либо вообще были такие компиляторы, которые этим правом пользовались?).

NI>В C++11 правила инициализации ссылок уже более вменяемые — там можно гарантированно забайндить ссылку непосредственно на return object функции pop


Почему нигде об этом не пишут? Как конкретно изменился wording инициализации ссылок?
Re[10]: Exception safe T Stack::pop() ?
От: N. I.  
Дата: 22.08.18 11:20
Оценка:
Максим Рогожин:

МР>Интересно почему это изначально сделано не было?


Это риторический вопрос?
Re[10]: Exception safe T Stack::pop() ?
От: N. I.  
Дата: 22.08.18 11:21
Оценка: 6 (1)
σ:

NI>>- из-за исключения при конструировании временного объекта, который компилятору позволялось создавать в контексте инициализации ссылки t (интересно, когда-либо вообще были такие компиляторы, которые этим правом пользовались?).


NI>>В C++11 правила инициализации ссылок уже более вменяемые — там можно гарантированно забайндить ссылку непосредственно на return object функции pop


σ>Почему нигде об этом не пишут?


А ты везде проверил?

σ>Как конкретно изменился wording инициализации ссылок?


C++98/03 [dcl.init.ref] / 5:

....
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined):

    — The reference is bound to the object represented by the rvalue (see 3.10) or to a subobject within that object.

    — A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a subobject within the temporary. [Footnote: Clearly, if the reference initialization being processed is one for the first argument of a copy constructor call, an implementation must eventually choose the first alternative (binding without copying) to avoid infinite recursion.]


C++11 [dcl.init.ref] / 5:

....
— If the initializer expression

    — is an xvalue, class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or

    — has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an xvalue, class prvalue, or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3”,

then the reference is bound to the value of the initializer expression in the first case and to the result of the conversion in the second case (or, in either case, to an appropriate base class subobject). ....


C++17 [dcl.init.ref] / 5.2.1:

— If the initializer expression

(5.2.1.1) — is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or

(5.2.1.2) — has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see 16.3.1.6),

then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” (7.5) and the temporary materialization conversion (7.4) is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).

Re[11]: Exception safe T Stack::pop() ?
От: Максим Рогожин Россия  
Дата: 22.08.18 15:58
Оценка:
Здравствуйте, N. I., Вы писали:

МР>>Интересно почему это изначально сделано не было?


NI>Это риторический вопрос?


Ну а какой смысл реализовывать этот код
class Stack {
public:
   T pop();
};

T t = stack.pop();

как вызов конструктора копирования временного объекта, в то время как можно просто конструировать объект в памяти выделенной под t?
Re: Exception safe T Stack::pop() ?
От: c-smile Канада http://terrainformatica.com
Дата: 22.08.18 18:28
Оценка: -1
Здравствуйте, Максим Рогожин, Вы писали:

МР>Привет!


МР>Можно ли сделать exception-safe T Stack::pop()?


Можно:

template<class T>
T Stack<T>::Pop() {
  if( vused == 0 )
    return T();
  else
    return v_[vused--];
}
Отредактировано 22.08.2018 18:43 c-smile . Предыдущая версия .
Re[2]: Exception safe T Stack::pop() ?
От: σ  
Дата: 24.08.18 22:30
Оценка:
Авторство кода? Лицензия? Запатентован?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.