在C ++中,什么类别(lvalue,rvalue,xvalue等)可以产生类类型的临时表达式? [英] In C++, what categories (lvalue, rvalue, xvalue, etc.) can expressions that produce temporaries of class type fall into?

查看:214
本文介绍了在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&运算符<< (std :: ostream& os,const Foo& f);

private:
int data;
};

std :: ostream&运算符<< (std :: ostream& os,const Foo& f)
{
return(os< fdata);
}

Foo操作符+(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值ref)< std :: endl;
}

int main()
{
//获取对象的标识
std :: cout< Foo(5).get_addr()< std :: endl; //可以写& Foo(5)
//通过重载&
// overload resolution
bar(Foo(5)); // prints r-value ref

//默认复制赋值
std :: cout<< (Foo(78)= Foo(86))< std :: endl; // prints 86

//改变操作
std :: cout<< (++ Foo(5))< std :: endl; // prints 6

//更多的变异操作
std :: cout<< (++(Foo(78)+ Foo(86)))< std :: endl; // prints 165
// overload resolution
bar((Foo(78)+ Foo(86))); //打印r值ref
}

表达式如Foo一般价值?事实上,我可以调用get_addr()这些表达式意味着他们有身份?或者,我不能应用默认& -operator(我的意思是非重载)的事实意味着他们没有身份,因此是prvalues?



也公平地说,产生的值的可变性通过产生它的表达式与该值分类正交。

解决方案

em> 每个表达式只有一个,并且只有一个:




  • lvalue

  • xvalue

  • prvalue



表示为lvalues和xvalues的联合统称为 glvalues



作为xvalues和prvalues的表达式的并集称为 rvalues



因此,xvalue表达式被称为glvalues和rvalues。



答案正确地说明了我用上面的单词描述的关系,并且也在C ++标准的3.10节中找到,版本C ++ 11及以上。



我上面所说的一切,我怀疑OP已经知道,只是从这个问题的标题的措辞。 p>




琐事:


Bjarne Stroustrup发明了这种表达式的分类,并且在
这样做可能保存了从核心工作组中的
崩溃的整个右值引用建议。我将永远感激。







我要添加的是一种发现对于自己,三个底部分类类型中的哪个表达式落入: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 =类型的值的表达式+ 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是一个< expression_name< decltype(some_expression)>()<< '\\\
';

它会回答这个OP的问题。例如:

  int main()
{
std :: cout< Foo(5)是< expression_name< decltype(Foo(5))>()<< '\\\
';
std :: cout<< Foo(5).get_addr()是< expression_name< decltype(Foo(5).get_addr())>()<< '\\\
';
std :: cout<< Foo(78)= Foo(86)是<< expression_name< decltype(Foo(78)= Foo(86))>()< '\\\
';
std :: cout<< ++ Foo(5)是一个< expression_name< decltype(++ Foo(5))>()<< '\\\
';
std :: cout<< ++(Foo(78)+ Foo(86))是< expression_name< decltype(++(Foo(78)+ Foo(86)))>() '\\\
';
std :: cout<< Foo(78)+ Foo(86)是< expression_name< decltype(Foo(78)+ Foo(86))>()< '\\\
';
std :: cout<< std :: move(Foo(5))是< expression_name< decltype(std :: move(Foo(5)))>()< '\\\
';
std :: cout<< std :: move(++ Foo(5))是< expression_name< decltype(std :: move(++ Foo(5)))>()< '\\\
';
}

对我打印:


$ b b

  Foo(5)是类型为Foo的prvalue表达式
Foo(5).get_addr()是类型为void *
的prvalue表达式78)= Foo(86)是类型Foo的左值表达式
++ Foo(5)是类型Foo的左值表达式
++(Foo(78)+ Foo类型Foo的左值表达
Foo(78)+ Foo(86)是类型Foo的prvalue表达式
std :: move(Foo(5))是类型Foo的xvalue表达式
std :: move(++ Foo(5))是类型为Foo的xvalue表达式

在使用这个函数时要小心:



decltype(variable_name)会给出声明的类型变量名。如果您想要在使用 variable_name (而不是其声明的类型)时发现表达式的值类别,那么您需要添加额外当在 decltype 中使用时,括号(variable_name)即:

  decltype((variable_name))

表达式 variable_name 的类型,而不是 variable_name



例如:

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

这将错误地输出:

  foo是类型为Foo的xvalue表达式

decltype

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

foo 一种表达。现在的输出是:

  foo是类型Foo的左值表达式

如果您不确定是否需要添加括号或不能获得正确的答案,那么只需添加它们。添加它们不会使正确的答案错误 - 除非你正在寻找一个变量的声明类型,而不是表达式的类型。在后一种情况下,您需要一个密切相关的函数: type_name< T>()


Here is some example code:

#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
}

Are expressions like Foo(5) prvalues or general rvalues? Does the fact that I can call get_addr() on these expressions mean that they have identity? Or does the fact that I cannot apply the default &-operator (I mean non-overloaded) mean that they do not have identity and are therefore prvalues?

Is it also fair to say that mutability of the produced value via the expression that produced it is orthogonal to this value-classification?

解决方案

Every expression is one, and only one, of:

  • lvalue
  • xvalue
  • prvalue

The union of expressions that are lvalues and xvalues are known collectively as glvalues.

The union of expressions that are xvalues and prvalues are known collectively as rvalues.

Thus xvalue expressions are known both as glvalues and rvalues.

The handy diagram found in Alf's answer correctly illustrates the relationship I've described with words above, and is also found in section 3.10 of the C++ standards, versions C++11 and above.

Everything I've said above, I suspect the OP already knew, just from the wording of the title of this question.


Trivia:

Bjarne Stroustrup invented this classification of expressions, and in doing so perhaps saved the entire rvalue reference proposal from collapsing in the Core Working Group. I will be forever grateful.


What I'm adding is a way to discover for yourself which of the three bottom classification categories any expression falls into: lvalue, xvalue or 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;
}

The above function can be used like:

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

And it will answer this OP's question. For example:

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';
}

For me prints out:

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

One area to be careful of in the use of this function:

decltype(variable_name) will give the declared type of the variable name. If you want to discover the value category of the expression when variable_name is used (as opposed to its declared type), then you need to add extra parentheses around (variable_name) when used in decltype. That is:

decltype((variable_name))

is the type of the expression variable_name, and not the declared type of variable_name.

For example given:

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

This will erroneously output:

foo is a xvalue expression of type Foo

Add the extra parentheses to the decltype:

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

to convert foo from a type name into an expression. Now the output is:

foo is a lvalue expression of type Foo

If you are unsure whether you need to add parentheses or not to get the correct answer, then just add them. Adding them won't make a correct answer wrong -- unless you are looking to get the declared type of a variable, and not the type of an expression. And in that latter case, you want a closely related function: type_name<T>().

这篇关于在C ++中,什么类别(lvalue,rvalue,xvalue等)可以产生类类型的临时表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆