Pages

About Me - 关于我

My photo
Madison, WI, United States
Joy Young ~~

2011/04/19

TheTricky Parts of Rvalue Reference and Perfect Forwarding.

I have read two articles about these stuffs. They are good. Here are the links:
I would not like to spend much time whenever I want to look up some useful information from these two articles, so I summarize something useful for my future reference.

1. Rvalue reference is not rvalue but reference
If you try to change the value of rvalue reference, it affects the value of the object it is referencing.

2. move semantic is awesome, but not irreplaceable.

Alternatively, you can use the static_cast keyword to cast an lvalue to an rvalue reference, as shown in the following example:

int main()
{
       MemoryBlock block;
       g(block);
       g(static_cast<MemoryBlock&&>(block));//as good as move
}

3. The compiler treats a named rvalue reference as an lvalue and an unnamed rvalue 
reference as an rvalue.
void g(X&& t); // A
void g(X& t);      // B

void h(X&& t)
{
       g(t);
}

Though t is declared like rvalue reference, but it is named, so g() always treats t as lvalue. If we changed the call to g(std::move(t)) then it would always call the rvalue-reference overload.
 
4. What does std::forward do?
When T is an lvalue reference, std::forward<T> is a no-op: it just returns its argument. 
When we pass a temporary to f, so T is just plain X. In this case,  std::forward<T>(t) is equivalent to static_cast<T&&>(t): it ensures that the argument is forwarded as an rvalue reference
Note that, it does not have anything to do with t. How it works is related to T solely.
5. What does std::move do? 
 Based on my understanding alone, its function is nothing more than a static_cast<T&&>()
Here I found some evidence: 
template<typename T>
remove_reference<T>::type&& move(T&& a)
{
       return a;
}

By Jonathan Caves

Jonathan Caves - Visual C++ Compiler Team

remove_reference here is a class from boost, it removes the reference symbol & and add two more instead!

6.  Perfect forwarding is unique to template functions: we can't do this with a non-template function such as h.
To understand this, let’s see how the template deduce the parameters:
First of all, we need to write a template this way:
template <typename T> void print_type_and_value(T&& t)

The tricky part is the T&& in the function. According to different form of T, the T&& will generate different types. See:
int main()
{
      // The following call resolves to:
      // print_type_and_value<string&>(string& && t)
      // Which collapses to:
      // print_type_and_value<string&>(string& t)
      string s1("first");
      print_type_and_value(s1);

      // The following call resolves to:
      // print_type_and_value<const string&>(const string& && t)
      // Which collapses to:
      // print_type_and_value<const string&>(const string& t)
      const string s2("second");
      print_type_and_value(s2);

      // The following call resolves to:
      // print_type_and_value<string&&>(string&& t)
      print_type_and_value(string("third"));

      // The following call resolves to:
      // print_type_and_value<const string&&>(const string&& t)
      print_type_and_value(fourth());
}

The deduction rule:


Expanded type
Collapsed type
T& &
T&
T& &&
T&
T&& &
T&
T&& &&
T&&

Now let’s go back to the discussion of trying perfect forwarding in non-template function:
Within a function that takes its arguments as rvalue references, the named parameter is treated as an lvalue reference. Consequently the call to g(t) from h always calls the lvalue overload. If we changed the call to g(std::forward<X>(t)) then it would always call the rvalue-reference overload. The only way to do this with "normal" functions is to create two overloads: one for lvalues and one for rvalues.
Finally, I attached this piece of code for quick testing.

#include <iostream>
using namespace std;
struct X{int i;};
void g(X&& t){ t.i = -1; cout<<"&&"<<endl;}; // A
void g(X& t){ t.i = -2; cout<<"&"<<endl;}      // B

template<typename T>
void f(T&& t){    g(std::forward<T>(t));}
void h1(X&& t){   g(t);}
void h2(X& t){    g(std::forward<X>(t));}
void h3(X& t){    g(std::forward<X&>(t));}
void h4(X& t){    g(std::move(t));}

int main()
{
     
      X x;
      x.i = 0;
      f(x);   // 1
      f(X()); // 2
      h1(X()); // 3
      h2(x); // 3
      cout<<x.i<<endl;
      h3(x);
      cout<<x.i<<endl;
      h4(x);
      cout<<x.i<<endl;
      getchar();
}










2011/04/06

C++嵌套类

1  1. 嵌套类的名字只在外围类可见。
  2. 类的私有成员只有类的成员和友元可以访问,因此外围类不可以访问嵌套类的私有成员。嵌套类可以访问外围类的成员(通过对象、指针或者引用)。
  3.一个好的嵌套类设计:嵌套类应该设成私有。嵌套类的成员和方法可以设为 public
    4.嵌套类可以直接访问外围类的静态成员、类型名( typedef )、枚举值。
   5. 嵌套类定义的名字解析过程:
出现在名字使用点前的嵌套类的声明。
出现在名字使用点前外围类的声明。
嵌套类定义前名字空间域的声明。

 

66. 嵌套类的成员定义中的名字解析过程:
成员函数局部声明。
嵌套类成员的声明。
外围类成员的声明。
成员函数定义前名字空间域中出现的声明。

2011/04/03

哎,又想骑车去旅行了


我很懒,但是有颗不安分的心。我想过着慵懒的生活,却又不甘平凡。
保研前,一直想保研,当真发现自己能保研了,却又觉着无趣了,然后准备出国,有病是不?
懒的运动,篮球足球都不爱,因为运动量太大,太累,结果大二花了十天灰头土脸的从北京骑车600多公里回了趟家,有病是不?
美国的生活很麻烦,各种事情,各种不顺,压力很大,也没时间。自己每天回家就累的啥也不想干了,但是,突然又想骑车旅行了。
非运动型人士,骑车旅行对我来说,本应更像一个概念而非实践。不过一件事,虽然笨了点,慢了点,但只要决定去做,总还是能做成的。
上次骑了650公里。这次,如果有可能,想骑个夸张的,从东海岸骑到西海岸去。初步估计,有4500公里。以我的运动能力,大概需要40多天,才能完成。太不切实际了是么?我也觉着有点,但是,如果拿定主意去做,其实不难。我需要的仅仅是:
1.  1. 1个半月的假期
2.  2. 一两位同伴
3.  3. 10000美金
其他的,就是周详的计划,调查,问询。上回十天的旅行,做了15天计划。这回40天的旅程,也要做2个月调查计划吧。也许老美和中国不太一样,比如没自行车道,比如中部无旅馆,有野兽,但这些都不会构成根本威胁。必须的仅仅是之上3条。
哈,大学那次,从有构想,但遇到志同道合之人,到付诸实施,过了两年吧。这个构想,不知道什么时候能实现,今天就放这里了,希望34年后的某天,我会再次出发。