30/09/2018, 20:11

Em không hiểu dấu && trong đoạn code dưới, Cao nhân nào chỉ giáo em với

template <class Fn, class... Args,
          class = typename std::enable_if<
                  !std::is_same<typename std::decay<Fn>::type,
                                my_thread>::value>::type>
explicit my_thread (Fn&& fn, Args&&... args) 
       : thread(std::forward<Fn>(fn), std::forward<Args>(args)...) { }
Phan Hoàng viết 22:15 ngày 30/09/2018

In C++03 (and before), temporaries (termed “rvalues”, as they often lie on the right side of an assignment) were intended to never be modifiable — just as in C — and were considered to be indistinguishable from const T& types; nevertheless, in some cases, temporaries could have been modified, a behavior that was even considered to be a useful loophole.[9] C++11 adds a new non-const reference type called an rvalue reference, identified by T&&. This refers to temporaries that are permitted to be modified after they are initialized, for the purpose of allowing “move semantics”.

Là reference tơi rvalue. rvalue không thể thay đổi giá trị, nhưng reference thì thay đổi được.

Hoàng Kiên viết 22:25 ngày 30/09/2018

Bác có thể lấy ví dụ cho em dễ hiểu hơn được không. Lơ tơ mơ quá. Tks ạ

Phan Hoàng viết 22:27 ngày 30/09/2018

Trước hết, trong C++ có 2 khái niệm rvalue (right) và lvalue (left). lvalue tham chiếu tới 1 object tồn tại, còn rvalue là một giá trị tạm thời và sẽ bị clear khi ra khỏi context dùng nó.

int i, j, *p;
i=9; //i là lvalue còn 9 là rvalue
j*4 = 16; // khai báo này sai vì j*4 không phải left value
 *p = i*4; //đố các bạn cái này đúng hay sai? 

Giờ chúng ta nói về reference, có 2 loại reference left (&) và right (&&)
Left reference (Cái này khá dễ hiểu, đại khái reference là address của object)

struct Person
{
    char* Name;
    short Age;
};

int main()
{
   // khai báo 1 object
   Person myFriend;
  // Khai báo một left reference trỏ tới myFriend object
   Person& rFriend = myFriend;
   //thay đổi giá trị của chính object và reference
   myFriend.Name = "Bill";
   rFriend.Age = 40;
   //in giá trị
   cout << rFriend.Name << " is " << myFriend.Age << endl;
}

Output: Bill is 40.

Right reference:
Cái này được đưa ra để dùng cho một trường hợp gọi là “move semantic”
Giả sử phép toán này: string s = string(“h”) + “e” + “f” + “g”;
Trước C++11, phép + đầu tiên sẽ trả về một rvalue tạm (ở đây sẽ là chuỗi “he”), phép cộng tiếp theo do rvalue tạm đã bị clear nên nó sẽ hông biét được string gốc giờ là lvalue hay rvalue. Nếu là rvalue thì nó không thể bị thay đổi. Giải quyết sao?

Một ví dụ nữa về việc insert 1 element vào Vector. Giờ giả sử không có khái niệm right reference, nếu dung lượng Vector hết, Vector sẽ phải phân bổ lại memory để nhét được thêm element mới vào, gọi copy constructor để copy đống element cũ sang bên Vector mới, destroy element cũ, destroy memory Vector cũ, … chà chà, khá là tốn memory (gấp 2) và một loạt thao tác thừa (copy). Nếu có có khái niệm right reference, Vector sẽ được phân bổ động memory và do đó không cần thao tác copy luôn ^^

//ví dụ về một generic coding
template <typename T, typename A1, typename A2>
T* factory(A1& a1, A2& a2)
{
   return new T(a1, a2);
}

Giờ muốn gọi cái factory này thì chúng ta phải làm thế này:

int a = 4, b = 5;
W* pw = factory<W>(a, b);

chứ nếu gọi

W* pw = factory<W>(4, 5); //ngoẻo vì 4,5 là rvalue

Giải pháp:

  • C1: viết thêm 1 overloading function nữa, thay cho generic variable kia, mình dùng primitive type (tốn thêm 2 dòng code)
  • C2: sử dụng khái niệm right refernce
template <typename T, typename A1, typename A2>
T* factory(A1&& a1, A2&& a2)
{
   return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

Hàm std::forward dùng để forward/move params sang constructor của class. Ngắn hơn được 2 dòng ^^

Phan Hoàng viết 22:13 ngày 30/09/2018
template <class Fn, class... Args,
          class = typename std::enable_if<!std::is_same<typename std::decay<Fn>::type, my_thread>::value>::type>

explicit my_thread (Fn&& fn, Args&&... args) 
       : thread(std::forward<Fn>(fn), std::forward<Args>(args)...) { }

B1: khai báo 1 template, với constructor được build từ 1 object Fn (constructor) và các arguments động (truyền bao nhiêu vào cũng được). enable_if loại bỏ constructor sử dụng overload.
B2: khai báo my_thread sử dụng right reference để nó forward sang template constructor.

minh tran viết 22:11 ngày 30/09/2018

chà kiến thức hay quá giờ mới biết có sự tồn tại của rvalue reference

Bài liên quan
0