2013-05-04 68 views
5

Tôi muốn chuyển (các) tham số (một số loại cụ thể, giả sử int) đến hàm thành viên theo tham chiếu r hoặc giá trị l (const). Giải pháp của tôi là:rvalue hoặc lvalue (const)

#include <type_traits> 
#include <utility> 

struct F 
{ 
    using desired_parameter_type = int; 

    template< typename X, typename = typename std::enable_if< std::is_same< typename std::decay<X>::type, desired_parameter_type >::value >::type > 
    void operator() (X && x) const 
    { 
     // or even static_assert(std::is_same< typename std::decay<X>::type, desired_parameter_type >::value, ""); 
     std::forward<X>(x); // something useful 
    } 
}; 

Một ngoại lệ khác là tại đây http://pastebin.com/9kgHmsVC.

Nhưng nó quá dài. Làm thế nào để làm điều đó một cách đơn giản hơn?

Có lẽ tôi nên sử dụng chồng chất std::remove_referencestd::remove_const thay vì std::decay, nhưng chỉ đơn giản hóa ở đây.

+0

Không có điểm nào chuyển một kiểu đơn giản như 'int' theo rvalue. Trong trường hợp tổng quát hơn, tôi tin C++ 11 có một trường hợp đặc biệt cho điều này, trong đó tham số rvalue sẽ được đọc dưới dạng tham số rvalue * hoặc * lvalue. Tôi không thể nhớ chính xác tình huống mà áp dụng mặc dù. – Dave

+0

Vì vậy, bạn muốn 'x' là tham chiếu rvalue (nếu tham chiếu rvalue được truyền) hay tham chiếu lvalue tới' const'? Tôi hiểu chính xác, rằng 'X &&' sẽ không được chấp nhận cho bạn bởi vì nó sẽ là một tham chiếu lvalue để không 'const' khi một lvalue được thông qua? –

+0

Ở đây chúng tôi đi, tôi tin rằng bạn đang cố gắng làm điều này: http://thbecker.net/articles/rvalue_references/section_07.html được giải thích bằng cách sử dụng giá trị trong trang tiếp theo. Yell nếu tôi hiểu lầm câu hỏi. – Dave

Trả lời

4

Nếu tôi hiểu chính xác câu hỏi của bạn, bạn muốn có một hàm duy nhất có tham số là tham chiếu rvalue (trong trường hợp giá trị được cung cấp) hoặc tham chiếu lvalue đến const (trong trường hợp được cung cấp giá trị).

Nhưng chức năng này sẽ làm gì? Vâng, vì nó phải có khả năng xử lý cả hai trường hợp, bao gồm trường hợp một lvalue được cung cấp, nó không thể sửa đổi đầu vào của nó (ít nhất không phải là tham số x) - nếu nó đã làm, nó sẽ vi phạm ngữ nghĩa của Tham chiếu const.

Nhưng sau đó một lần nữa, nếu nó không thể thay đổi trạng thái của tham số, không có lý do để cho phép tham chiếu rvalue: thay vì để x là tham chiếu lvalue đến const mọi lúc. Tham chiếu lvalue đến const có thể liên kết với các giá trị, vì vậy bạn sẽ được phép chuyển cả giá trị và giá trị.

Nếu ngữ nghĩa của các hàm được khác nhau dựa trên những gì được thông qua, sau đó tôi sẽ nói nó làm cho ý nghĩa hơn để viết hai chức năng như vậy: một trong đó có một tài liệu tham khảo rvalue và một trong đó có một tài liệu tham khảo giá trị trái để const.

+0

constness là tùy chọn (vì vậy tất cả '&&', '&', 'const &' được phép) – Orient

+1

@Dukales: Tôi không hiểu: (Ý của bạn là gì? Nếu hàm này có thể hoạt động với 'const' các đối tượng, nó có nghĩa là nó sẽ không cố gắng sửa đổi 'x'. Nhưng sau đó một lần nữa, điều này có nghĩa là bạn không cần tham chiếu rvalue –

+0

Bạn có biết về lập trình meta không?Cho phép giả định, các hàm đó, được truyền các tham số tới, sẽ xác định việc cần làm với (cách gửi đi) "sự tham chiếu" và độ chụm cụ thể của chúng. – Orient

1

Như Andy đã đề cập, điều quan trọng ở đây là những gì bạn thực sự có thể làm bên trong chức năng của bạn sẽ có ý nghĩa.

  • Bạn có thể chuyển tiếp đối số cho một hàm khác. Trong trường hợp này, việc sử dụng một mẫu không quan trọng, bởi vì nếu loại tham số sai được cho, nó sẽ vẫn tạo ra một lỗi biên dịch (các kiểu sai được gửi tới hàm thứ hai). Bạn có thể sử dụng template <typename T> blah (T && x).
  • Bất kỳ điều gì khác sẽ yêu cầu bạn viết mã khác nhau tùy thuộc vào việc đó là tham chiếu giá trị hay không, vì vậy bạn cần phải viết 2 hàm: blah (const int & x)blah (int && x).

Tôi cho rằng bạn phải thử tùy chọn đầu tiên và bạn đang cố gắng làm cho bất kỳ lỗi trình biên dịch nào có thể thân thiện với người dùng hơn. Vâng, tôi muốn nói nó không đáng giá; lập trình viên sẽ vẫn thấy danh sách "được gọi bởi ..." trong đầu ra của bất kỳ trình biên dịch nào.

+0

Theo chức năng có thể có nghĩa là một ctor với một danh sách khởi tạo lớn. – Orient

+0

@Dukales: Nhưng đó chỉ là chuyển tiếp, vì vậy bạn có thể sử dụng mẫu và vẫn gặp phải lỗi trình biên dịch nếu nó được gọi sai. – Dave

1

Thực ra, đây là một câu hỏi rất hay. Cho đến nay tôi cũng đã sử dụng thủ thuật tham chiếu chung cộng với búa enable_if. Ở đây tôi trình bày một giải pháp không sử dụng các khuôn mẫu và sử dụng cách viết lvalue như một sự thay thế.

Dưới đây là ví dụ thực tế khi tình huống phát sinh bằng cách sử dụng ví dụ được sử dụng tại chỗ ofstream không thể (hoặc rất khó) trong C++ 98 (tôi sử dụng ostringstream trong ví dụ để làm rõ hơn).

Trước tiên, bạn sẽ thấy một hàm về tham chiếu lvalue như thường thấy trong C++ 98.

#include<iostream> 
#include<sstream> 
struct A{int impl_;}; 

std::ostringstream& operator<<(std::ostringstream& oss, A const& a){ 
    oss << "A(" << a.impl_ << ")"; // possibly much longer code. 
    return oss; 
} 
// naive C++11 rvalue overload without using templates 
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){ 
    oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition. 
    return oss; 
} 

int main() { 

    A a{2}; 
    {// C++98 way 
     std::ostringstream oss; 
     oss << a; 
     std::cout << oss.str() << std::endl; // prints "A(2)", ok" 
    } 
    {// possible with C++11, because of the rvalue overload 
     std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok 
    } 
} 

Như bạn có thể thấy trong C++ 11, chúng tôi có thể đạt được những gì chúng tôi không thể có trong C++ 98. Đó là sử dụng ostringstream (hoặc ofstream) tại chỗ. Bây giờ đến câu hỏi OP, hai quá tải trông rất giống nhau, cả hai có thể được tham gia vào một?

Một tùy chọn là sử dụng tham chiếu chung (Ostream&&) và tùy chọn với enable_if để hạn chế loại. Không phải rất thanh lịch.

Những gì tôi tìm thấy bằng cách sử dụng ví dụ "thế giới thực" này là nếu muốn sử dụng cùng một mã cho ref lvalue và ref rvalue là vì có lẽ bạn có thể chuyển đổi một cái khác!

std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){ 
    return operator<<(oss, a); 
} 

này trông giống như một chức năng vô hạn đệ quy, nhưng nó không phải là vì oss là một tài liệu tham khảo giá trị trái (có, nó là một tài liệu tham khảo giá trị trái bởi vì nó có một cái tên). Vì vậy, nó sẽ gọi quá tải khác.

Bạn vẫn phải viết hai hàm nhưng một hàm có mã mà bạn không phải duy trì. Tóm lại, nếu "có ý nghĩa" © để áp dụng một hàm cho cả tham chiếu và giá trị rvalue (không const) cũng có nghĩa là bạn có thể chuyển đổi giá trị rvalue thành một giá trị và do đó bạn chuyển tiếp đến một hàm duy nhất. Lưu ý rằng "nó có ý nghĩa" phụ thuộc vào ngữ cảnh và ý nghĩa của mã dự kiến, và nó là một cái gì đó mà chúng ta phải "nói" trình biên dịch bằng cách explictly gọi quá tải lvalue.

Tôi không nói rằng điều này là tốt hơn so với sử dụng tham chiếu phổ quát, tôi nói đó là một thay thế và cho là ý định rõ ràng hơn.

Mã có thể chỉnh sửa tại đây: http://ideone.com/XSxsvY. (Phản hồi được hoan nghênh)

Các vấn đề liên quan