LINUX.ORG.RU

Каким образом оно возвращает lvalue?

 , ,


0

3
template<class T>
struct V {
  V() = default;
  V(const T& v): val(v) {};
  T val;
};

struct A {
  V<int> a;
  int b;
};

V<int> wtf(const A& c) {
  return c.a;
}

int return_rvalue(const A& c) {
  return c.b;
}

int main() {
        A c;
        wtf(c) = 0xbabe;  //< Почему не ругается здесь?
        return_rvalue(c) = 0xdead;  //< Здесь ругается, как и положено 
}

Отловил неожиданную для себя проблему в коде, когда функция возвращается значение по значению, но каким-то образом присвоение этому временному объекту отрабатывает без ошибок, даже без варнингов. Как это вообще возможно? Ниже функция return_rvalue не компилируется, как ей и положено,я ожидал схожего поведения от wtf, но оказался неправ и был жестко наказан: присвоенное значение просто съедалось и исчезало в бездне небытия.

Думал, что проблема из-за implicit конструктора принимающего оборачиваемый тип, убрал его, заменил на перегруженный operator=, все осталось по прежнему.

Прошу помощи зала, спасибо.

Для пользовательских классов присваивание является всего лишь методом, а на rvalue объектах можно вызывать методы. Так что здесь нет lvalue, только некоторая непоследовательность в поведении между встроенными типами и типами, определёнными пользователем.

xaizek ★★★★★ ()

присвоенное значение просто съедалось и исчезало в бездне небытия.

А оно не в c.a.val попадает? Там же вроде конструктор определён в структуре. А wtf(c) = 1.2 должно вылетать с криками.

ziemin ★★ ()
Ответ на: комментарий от ziemin

Эх если бы :(

A c;
c.a.val = 0xdead;
wtf(c) = 0xbabe;  //< WHY IT DOESN'T FAIL?
std::cout << std::hex << c.a.val << std::endl;

выводит «dead»

redbaron ★★ ()
Ответ на: комментарий от xaizek

Какими костылям это можно обойти? Т.е. надо заставить компилятор ругаться при вызове любого non-const метода на rvalue объектах.

redbaron ★★ ()
Ответ на: комментарий от xaizek

Нашел в «Rvalue references for *this» отсюда, реализован только в gcc 4.8.1, что несмертельно, но неприятно, мне б 4.7 :)

Можно перегружать методы теперь еще и по lvalue/rvalue категории *this, что должно помочь. Спасибо за объяснение такого поведения.

redbaron ★★ ()
Ответ на: комментарий от redbaron

О, я тоже пытался найти «Rvalue referneces for *this», но выпало из головы название и искал не по тем словам.

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

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

Ну возвращать объекты я и не планировал, просто опечатался, но хотелось бы защититься от таких опечаток заранее.

Вообще странная история. После того, как добавили с стандарт этот самый «Rvalue referneces for *this», они решили, что все типы из стандартной библиотеки должны вести себя так же как элементарные типы, т.е. присваивание rvalue объектам должно быть запрещено (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2819.html) в итоге они прошлись по всем операторам присваивания всех типов и прилепили этот «&» в конце.

Т.е. все вместе они признали, что проблема существует и требует решения, но вместо того, чтобы добавить в стандарт запрет на присваивание rvalue для вообще всех типов,они мучались сами лепя этот «&» и заставили мучатся всех остальных во всем обозримом будущем.

ИМХО 99.999% кода который присваивает временным объектам другие значения все равно не рабочий и делается это по ошибке, какой смысл сохранять обратную совместимость с таким кодом? Те же кто делают это сознательно ради побочных эффектов заслуживают наказания в любом случае.

redbaron ★★ ()

Вызов метода присваивания может иметь любые побочные эффекты, потому wtf(c) = 0xbabe; не является бессмысленной операцией.

Pavval ★★★★★ ()
Ответ на: комментарий от Pavval

Вызов метода присваивания может иметь любые побочные эффекты, потому wtf(c) = 0xbabe; не является бессмысленной операцией.

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

DELIRIUM ☆☆☆☆☆ ()

Собеседование на позицию «C++ developer»

Если знаешь C++,
Покажи-ка свой анУс.
Коль диаметр слишком мал,
Серверов ты не писал,
Говнокод ты не дебаггил,
STL и boost не правил.
Приходи к нам через год,
Мейерсом расширь проход.

DELIRIUM ☆☆☆☆☆ ()
Ответ на: комментарий от redbaron

Ну, итоговый результат таких действий действительно выглядит странно, но если рассматривать как они к этому пришли, то понять можно. Ведь, следующий код не кажется неправильным:

std::thread(start).detach();
А с присваиванием, по сути тоже самое происходит:
foo() = bar;
// эквивалентно
foo().operator=(bar);
Делать исключение и запрещать только присваивание для rvalue, было бы на мой взгляд ещё большим костылём чем то, на что они пошли с «Rvalue references for *this», у которого также найдутся и другие полезные применения. Просто данное нововведение, «по счастливой случайности», позволяет решить этот бок с присваиванием.

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

std::thread(start).detach();

Ого, крутота, так можно. До этого я бы писал так:

std::thread t(start);
t.detach();

Век живи - век учись.

Я не знал, что конструктор возвращает ссылку на свой объект, я думал, там void.

DELIRIUM ☆☆☆☆☆ ()
Последнее исправление: DELIRIUM (всего исправлений: 1)
Ответ на: комментарий от DELIRIUM

Конструктор ничего не возвращает, выражение T(x1,x2,...) создаёт значение типа T и при этом явно вызывает конструктор.

Begemoth ★★★★★ ()
Последнее исправление: Begemoth (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.