Value category - prvalue po lewej stronie operatora

1

Cześć,

próbuję zrozumieć ideę idącą za value category i mam pewien problem.

Pierwszy przykład:

Otóż, jak na moje oko to ten kod powinien spowodować błąd podczas kompilacji (a co najmniej ostrzeżenie):

Link Wandbox: https://wandbox.org/permlink/OoIqDzPA9dy2LS3q

#include <iostream>

using namespace std;


void f(const int&& i) {
	std::cout << &i;
}

int main() {
	int a = 8;
	f(7);

	return 0;
}

Jednakże nic takiego się nie dzieje:

g++ prog.cc -Wall -Wextra -std=c++11 -pedantic

prog.cc: In function 'int main()':
prog.cc:11:6: warning: unused variable 'a' [-Wunused-variable]
  int a = 8;
      ^

./a.out 
0x7fffb87e2128

Jasne jest, że zmienna i jest prvalue, jednak kompilator pozwala pobrać jej adres (co jest chyba niedozwolone), dlaczego? (nie pytam o to, że tworzy zmienną tymczasową i dlatego może).

Drugi przykład:

Link Wandbox: https://wandbox.org/permlink/Z3j3W0W1xBWcLfmO

struct S {
    S& operator=(int) { 
        return *this;
    }
};


int main() {
    S() = 7;

	return 0;
}

Kompilacja przebiega bez żadnych ostrzeżeń, dlaczego? Przecież po obu stronach wyrażenia S() = 7; mamy prvalue.

Trzeci przykład:

Link Wandbox: https://wandbox.org/permlink/UWX0IrW51N2jeIR8

int main() {
    int &i = int();

	return 0;
}

Słusznie powoduje błąd kompilacji:

prog.cc:6:18: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
     int &i = int();

Jednakże drobna zmiana, powoduje że kompilator nie widzi problemu:

Link Wandbox: https://wandbox.org/permlink/4Nha8pJ9ThpOy5GO

int main() {
    const int &i = int();

	return 0;
}

A przecież jest to taka sama sytuacja - po prawej stronie prvalue, a po lewej lvalue przechowujące adres na prvalue.

Temporary materialization wchodzi w C++17, więc zakładam, że nie to jest powodem takiego zachowania. Czy są jakieś reguły, o których nie wiem?

1

Wewnątrz funkcji jest lvalue, do którego masz rvalue-ref. Wszystko co ma nazwę jest lvalue. i jest nazwą ;​)
Wg. mnie to jest całkiem sensowne, możesz np. przymować foo&& i zwracać (odpowiednio zmodyfikowane) foo&&

2 i 3)
Jest to rażąca niekonsekwencja, która musi zostać utrzymana, bo kompatybilność wsteczna :​( Również mnie to irytuje:

"foo"s = "bar"; // ok
"foo" = bar; // error

Jak chcesz w swoich funkcjach uniemożliwić takie przypisania, możesz użyć kwalifikatorów & i &&:

struct foo
{
    foo& operator=(int) & { return *this; }
    foo& operator=(int) && = delete;
};

foo a;
a = 42; // ok
foo{} = 42; // error

Temporary materialization

TIL

0
kq napisał(a):

2 i 3)
Jest to rażąca niekonsekwencja, która musi zostać utrzymana, bo kompatybilność wsteczna :​( Również mnie to irytuje:

Ok, tylko to jest UB, czy to wyspecyfikowali jakoś?

0

Zapisujesz do zmiennej tymczasowej, która zaraz znika. W pełni legalne, tylko bezsensowne.

0

Z każdym rokiem nauki C++ wiem o nim coraz mniej. Taki paradoks.

0

Takie operacje:
int a = 8;
to inicjalizacja a nie przypisanie i nie podlegają zasadom opisanym przez value category w odniesieniu do wyrażeń. Mają w pewnym stopniu swoje prawa np. http://en.cppreference.com/w/cpp/language/reference czy tu http://en.cppreference.com/w/cpp/language/initialization

0

Spoko, ale przecież​ o takim przypadku nie mówiliśmy, bo jest oczywisty.

0

Zostawiłem dla potomnych, gdyby ktoś kiedyś tutaj trafił. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1