Защо е позволено T() = T()?

Вярвам, че изразът T() създава rvalue (по стандарта). Следният код обаче се компилира (поне на gcc4.0):

class T {};

int main()
{
    T() = T();
}

Знам, че технически това е възможно, защото функциите-членове могат да бъдат извикани на временни елементи и горното е просто извикване на operator= на rvalue temporary, създаден от първия T().

Но концептуално това е като присвояване на нова стойност на rvalue. Има ли основателна причина това да е разрешено?

Редактиране: Причината да намирам това за странно е, че е строго забранено за вградени типове, но е позволено за дефинирани от потребителя типове. Например int(2) = int(3) няма да се компилира, защото това е "невалидна lvalue в присвояването".

Така че предполагам, че истинският въпрос е дали това донякъде непоследователно поведение е вградено в езика с причина? Или е там по някаква историческа причина? (Например би било концептуално по-правилно да се позволи само const членски функции да бъдат извиквани на rvalue изрази, но това не може да бъде направено, защото може да наруши някои съществуващ код.)


person Rimo    schedule 28.05.2010    source източник


Отговори (5)


Ето защо могат да бъдат внедрени няколко класа в стандартната библиотека. Помислете например за std::bitset<>::operator[]

// bit reference:
class reference {
  friend class bitset;
  reference();
public:
  ˜reference();
  reference& operator=(bool x);           // for b[i] = x;
  reference& operator=(const reference&); // for b[i] = b[j];
  bool operator˜() const; // flips the bit
  operator bool() const;  // for x = b[i];
  reference& flip();      // for b[i].flip();
};

reference operator[](size_t pos); // for b[i];

Ако направите bits[i] = true, вие точно присвоявате някаква стойност на rvalue от тип клас. Проксито, което се връща от operator[], може да има достъп до битовете, които са пространство, ефективно пакетирано в цели числа.

person Johannes Schaub - litb    schedule 28.05.2010
comment
Това заедно с отговора на FredOverflow отговаря напълно на въпроса ми. Благодаря! - person Rimo; 30.05.2010

Това е позволено само поради претоварването на оператора и възможността да претоварите operator =, за да направите нещо по-фантастично, като отпечатване в конзолата или заключване на мютекс, или нещо друго.

person Inverse    schedule 28.05.2010
comment
И не само operator=, но и конструкторите. Има много странни конструкции в C++, където компилаторът просто вдига рамене и се надява, че знаете какво правите. :-) - person Owen S.; 28.05.2010

Да, вие присвоявате нова стойност на rvalue. По-точно, вие извиквате членската функция operator = на rvalue. След като не използвате вградения оператор за присвояване, защо смятате, че това трябва да е проблем? operator = е функция член на класа, която в повечето отношения е подобна на всяка друга функция член на класа, включително факта, че може да бъде извикана на rvalues.

Вероятно трябва да вземете предвид и факта, че "да бъдеш rvalue" е свойство на израз, а не свойство на обект. Вярно е, че изразът T() се оценява на rvalue. Независимо от това, временният обект, генериран от израза T(), все още е обект, който може да бъде достъпен и като lvalue. Например, може да бъде извикана друга членска функция на резултата от присвояването и тя ще види „новата“ (прясно присвоена) стойност на временния обект чрез *this lvalue

(T() = T()).some_member_function();

Можете също така да удължите живота на временния елемент, като прикачите const-reference към него const T& r = T() = T(); и стойността, която се вижда през r, ще бъде „новата“ стойност на обекта. Както Йоханес правилно отбеляза в своя коментар, това няма да го прикачи към временен.

person AnT    schedule 28.05.2010
comment
Да, мисля, че разбирам механизма на „как“ работят подобни операции. Но аз все още съм любопитен „защо“ дизайнерите на езика позволиха на r-стойностите (или временните, създадени от изрази на r-стойности) да бъдат мутирани като такива? Дали това е пропуск от тяхна страна или е допуснато умишлено (Може би е имало някои практически причини, които са достатъчно убедителни, за да оправдаят такова привидно непоследователно поведение между вградените типове и дефинираните от потребителя типове?) - person Rimo; 28.05.2010
comment
Само за да поясня, лично аз бих намерил за много по-малко изненадващо, ако само const членски функции бяха разрешени да бъдат извиквани на rvalue изрази. - person Rimo; 28.05.2010
comment
@Rhimo: Това побеждава разумни конструкции като Atomic(std::cout) << 1 << " uninterrupted output" << std::endl; - person MSalters; 28.05.2010
comment
Малък пропуск тук, мисля: const T& r = T() = T(); няма да работи. Опитвате се да обвържете препратката const към lvalue (T& върнато от оператора за присвояване на копие) и след това временното ще бъде унищожено. Lifetime се удължава само ако се опитате да го свържете към rvalue, която се отнася до временното. - person Johannes Schaub - litb; 28.05.2010

@Hans: спокойно, просто си мислех, че може да имате под ръка пример как да използвате правилно GetWindowPlacement от C# заедно с WinForms. Подозирам, че има някои капани, които трябва да се избягват.
person fredoverflow    schedule 28.05.2010

От един POV е непоследователен, но вие пренебрегвате колко е последователен: 1) ints и други вградени типове все още се държат както в C, 2) operator= на типове класове се държи като всеки друг метод, без да изисква още един специален случай.

Съвместимостта със C е високо ценена от началото на C++ и C++ вероятно нямаше да съществува днес без нея. Така че тази част като цяло е нещо добро.

Втората точка е подценена. Операторът без специален регистър= позволява на безсмисления код да "работи", но защо изобщо ни е грижа за безсмисления код? Боклук вътре, боклук вън. Настоящите правила му придават определено значение (UB тук би било лошо) с незначителна цена, доколкото някога съм виждал.

Като се имат предвид моите друтери, нещата ще бъдат опростени още повече, така че int() = int() ще бъде позволено. C++0x започва да се насочва в тази посока с rvalue-references, prvalues ​​и т.н.

person Community    schedule 28.05.2010
comment
C++0x започва да се насочва в тази посока с rvalue-references, prvalus и т.н. Hug? - person curiousguy; 25.12.2011