В C ++ какие категории (lvalue, rvalue, xvalue и т. Д.) Могут выражать выражения, которые производят временные classы типа classа?

Вот пример кода:

#include  class Foo { public: explicit Foo(int x) : data(x) {}; Foo& operator++() { data += 1; return *this; } void *get_addr() { return (void*)this; } friend Foo operator + (const Foo& lhs, const Foo& rhs); friend std::ostream& operator << (std::ostream& os, const Foo& f); private: int data; }; std::ostream& operator << (std::ostream& os, const Foo& f) { return (os << f.data); } Foo operator + (const Foo& lhs, const Foo& rhs) { return Foo(lhs.data + rhs.data); } void bar(Foo& f) { std::cout << "bar(l-value ref)" << std::endl; } void bar(const Foo& f) { std::cout << "bar(const l-value ref)" << std::endl; } void bar(Foo&& f) { std::cout << "bar(r-value ref)" << std::endl; } int main() { // getting the identity of the object std::cout << Foo(5).get_addr() << std::endl; // Can write &Foo(5) // by overloading & // overload resolution bar(Foo(5)); // prints r-value ref // default copy assignment std::cout << (Foo(78) = Foo(86)) << std::endl; // prints 86 // mutating operations std::cout << (++Foo(5)) << std::endl; // prints 6 // more mutating operations std::cout << (++(Foo(78) + Foo(86))) << std::endl; // prints 165 // overload resolution bar((Foo(78) + Foo(86))); // prints r-value ref } 

Являются ли выражения типа Foo (5) prvalues ​​или общие значения r? Может ли тот факт, что я могу вызвать get_addr () для этих выражений, означает, что у них есть личность? Или тот факт, что я не могу применить по умолчанию & -оператор (я имею в виду не перегруженный), означает, что они не имеют идентификатора и, следовательно, являются prvalues?

Справедливо ли справедливо также сказать, что изменчивость производимой ценности посредством выраженного ею выражения является ортогональной этой classификации значений?

Каждое выражение – это одно и только одно:

  • именующий
  • xvalue
  • prvalue

Объединение выражений, которые являются lvalues ​​и xvalues, известны в совокупности как glvalues .

Объединение выражений, которые являются значениями x и prvalues, известны вместе как rvalues .

Таким образом, выражения xvalue известны как как glvalues, так и rvalues.

Удобная диаграмма, найденная в ответе Альфа , правильно иллюстрирует взаимосвязь, описанную выше, и также находится в разделе 3.10 стандартов C ++, версий C ++ 11 и выше.

Все, что я сказал выше, я подозреваю, что ОП уже знал, как раз из формулировки названия этого вопроса.


Общая информация:

Бьярне Страуструп изобрел эту classификацию выражений, и при этом, возможно, удалось сэкономить все справочное предложение rvalue от рушится в основной рабочей группе. Я буду навсегда благодарен.


То, что я добавляю, – это способ узнать для себя, какая из трех нижних classификационных категорий имеет любое выражение: lvalue, xvalue или prvalue.

 #include  #include  #include  #ifndef _MSC_VER # include  #endif #include  #include  #include  template  std::string expression_name() { typedef typename std::remove_reference::type TR; std::unique_ptr own ( #ifndef _MSC_VER __cxxabiv1::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), #else nullptr, #endif std::free ); std::string r = own != nullptr ? own.get() : typeid(TR).name(); if (std::is_const::value) r += "const "; if (std::is_volatile::value) r += "volatile "; if (std::is_lvalue_reference::value) r = "lvalue expression of type " + r; else if (std::is_rvalue_reference::value) r = "xvalue expression of type " + r; else r = "prvalue expression of type " + r; return r; } 

Вышеупомянутая функция может использоваться как:

 std::cout << "some_expression is a " << expression_name() << '\n'; 

И он ответит на этот вопрос ОП. Например:

 int main() { std::cout << "Foo(5) is a " << expression_name() << '\n'; std::cout << "Foo(5).get_addr() is a " << expression_name() << '\n'; std::cout << "Foo(78) = Foo(86) is a " << expression_name() << '\n'; std::cout << "++Foo(5) is a " << expression_name() << '\n'; std::cout << "++(Foo(78) + Foo(86)) is a " << expression_name() << '\n'; std::cout << "Foo(78) + Foo(86) is a " << expression_name() << '\n'; std::cout << "std::move(Foo(5)) is a " << expression_name() << '\n'; std::cout << "std::move(++Foo(5)) is a " << expression_name() << '\n'; } 

Для меня распечатывается:

 Foo(5) is a prvalue expression of type Foo Foo(5).get_addr() is a prvalue expression of type void* Foo(78) = Foo(86) is a lvalue expression of type Foo ++Foo(5) is a lvalue expression of type Foo ++(Foo(78) + Foo(86)) is a lvalue expression of type Foo Foo(78) + Foo(86) is a prvalue expression of type Foo std::move(Foo(5)) is a xvalue expression of type Foo std::move(++Foo(5)) is a xvalue expression of type Foo 

Одной из областей, которые необходимо соблюдать при использовании этой функции:

decltype(variable_name) даст объявленный тип имени переменной. Если вы хотите узнать категорию значений выражения, когда используется имя variable_name (в отличие от объявленного типа), тогда вам нужно добавить дополнительные круглые скобки (variable_name) при использовании в decltype . То есть:

 decltype((variable_name)) 

это тип выражения variable_name , а не объявленный тип variable_name .

Например:

  Foo&& foo = Foo(5); std::cout << "foo is a " << expression_name() << '\n'; 

Это будет ошибочно выводить:

 foo is a xvalue expression of type Foo 

Добавьте дополнительные скобки в decltype :

  std::cout << "foo is a " << expression_name() << '\n'; 

для преобразования foo из имени типа в выражение. Теперь выход:

 foo is a lvalue expression of type Foo 

Если вы не уверены, нужно ли добавлять скобки или нет, чтобы получить правильный ответ, просто добавьте их. Добавление их не приведет к неправильному правильному ответу - если вы не хотите получить объявленный тип переменной, а не тип выражения. И в последнем случае вам нужна тесно связанная функция: type_name() .

Любое выражение в C ++ является либо значением lvalue, либо rvalue . Следовательно, вы запрашиваете classификации, которые являются значениями r. Для этого просмотрите рисунок, показывающий дерево classификаций, в стандарте C ++ 11 §3.10 / 1.

Таксономия категории выражений


Для получения дополнительной информации (без вникания в стандарт) см. Что такое rvalues, lvalues, ….


относительно

«Являются ли выражения типа Foo (5) rvalues ​​или prvalue”

значение prvalue необходимо для rvalue – поскольку это не может быть lvalue.

« Prvalue » («чистое» rvalue) является значением r, которое не является значением x », а значение x является результатом определенных видов выражений, содержащих ссылки rvalue». В вызове конструктора не создается ссылка rvalue, следовательно, это не значение xvalue , Таким образом, rvalue является prvalue, чистым значением rvalue.

Давайте будем гением компьютера.