c ++ 11 — В C ++, в какие категории (lvalue, rvalue, xvalue и т. д.) могут попадать выражения, которые производят временные выражения типа класса?

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

#include <iostream>

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) значениями или общими значениями? Означает ли тот факт, что я могу вызвать get_addr () для этих выражений, означает, что они имеют идентичность? Или тот факт, что я не могу применить по умолчанию &-оператор (я имею в виду не перегруженный) означает, что они не имеют идентичности и, следовательно, являются значениями?

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

9

Решение

каждый Выражение одно и только одно из:

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

Объединение выражений lvalues ​​и xvalues ​​известно под общим названием glvalues.

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

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

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

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


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

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


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

#include <type_traits>
#include <typeinfo>
#include <iostream>
#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <typename T>
std::string
expression_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> 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<TR>::value)
r += "const ";
if (std::is_volatile<TR>::value)
r += "volatile ";
if (std::is_lvalue_reference<T>::value)
r = "lvalue expression of type " + r;
else if (std::is_rvalue_reference<T>::value)
r = "xvalue expression of type " + r;
else
r = "prvalue expression of type " + r;
return r;
}

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

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

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

int main()
{
std::cout << "Foo(5) is a " << expression_name<decltype(Foo(5))>() << '\n';
std::cout << "Foo(5).get_addr() is a " << expression_name<decltype(Foo(5).get_addr())>() << '\n';
std::cout << "Foo(78) = Foo(86) is a " << expression_name<decltype(Foo(78) = Foo(86))>() << '\n';
std::cout << "++Foo(5) is a " << expression_name<decltype(++Foo(5))>() << '\n';
std::cout << "++(Foo(78) + Foo(86)) is a " << expression_name<decltype(++(Foo(78) + Foo(86)))>() << '\n';
std::cout << "Foo(78) + Foo(86) is a " << expression_name<decltype(Foo(78) + Foo(86))>() << '\n';
std::cout << "std::move(Foo(5)) is a " << expression_name<decltype(std::move(Foo(5)))>() << '\n';
std::cout << "std::move(++Foo(5)) is a " << expression_name<decltype(std::move(++Foo(5)))>() << '\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<decltype(foo)>() << '\n';

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

foo is a xvalue expression of type Foo

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

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

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

foo is a lvalue expression of type Foo

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

17

Другие решения

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

Выражение категории таксономии


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


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

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

prvalue необходимо rvalue — потому что оно не может быть lvalue.

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

6

По вопросам рекламы [email protected]