Вот пример кода:
#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 () для этих выражений, означает, что они имеют идентичность? Или тот факт, что я не могу применить по умолчанию &-оператор (я имею в виду не перегруженный) означает, что они не имеют идентичности и, следовательно, являются значениями?
Справедливо ли также сказать, что изменчивость произведенной стоимости через выражение, которое ее произвело, ортогональна этой классификации стоимости?
каждый Выражение одно и только одно из:
Объединение выражений 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>()
.
Любое выражение в C ++ или именующий или Rvalue. Следовательно, вы запрашиваете классификации, которые являются значениями. Для этого просмотрите рисунок, показывающий дерево классификаций в стандарте C ++ 11 § 3.10 / 1.
Для получения дополнительной информации (без углубления в стандарт) см. Что такое значения, значения, ….
относительно
«Являются ли выражения типа Foo (5) rvalues или prvalue»
prvalue необходимо rvalue — потому что оно не может быть lvalue.
prvalue «(« Чистое »значение) — это значение, которое не является значением x», и xvalue является «результатом определенных видов выражений, включающих
Ссылки на rvalue. Вызов конструктора не создает ссылку на rvalue, следовательно, это не xvalue. Таким образом, значение — это значение, чистое значение.