Lợi thế của việc sử dụng tham chiếu chuyển tiếp trong vòng lặp for dựa trên phạm vi là gì?


115

const auto&sẽ đủ nếu tôi muốn thực hiện các thao tác chỉ đọc. Tuy nhiên, tôi đã va vào

for (auto&& e : v)  // v is non-const

một vài lần gần đây. Điều này khiến tôi tự hỏi:

Có thể trong một số trường hợp góc khuất có một số lợi ích về hiệu suất trong việc sử dụng tham chiếu chuyển tiếp, so với auto&hoặc const auto&?

( shared_ptrlà nghi phạm cho các trường hợp góc khuất)


Cập nhật Hai ví dụ mà tôi tìm thấy trong mục yêu thích của mình:

Bất kỳ bất lợi nào của việc sử dụng tham chiếu const khi lặp qua các kiểu cơ bản?
Tôi có thể dễ dàng lặp lại các giá trị của bản đồ bằng vòng lặp for dựa trên phạm vi không?

Vui lòng tập trung vào câu hỏi: tại sao tôi muốn sử dụng tự động && trong các vòng lặp dựa trên phạm vi?


4
Bạn có thực sự thấy nó "thường xuyên" không?
Các cuộc đua ánh sáng trong quỹ đạo vào

2
Tôi không chắc có đủ ngữ cảnh trong câu hỏi của bạn để tôi đánh giá mức độ "điên rồ" mà bạn đang thấy.
Các cuộc đua ánh sáng trong quỹ đạo vào

2
@LightnessRacesinOrbit Câu chuyện ngắn: tại sao tôi muốn sử dụng auto&&trong các vòng lặp for dựa trên phạm vi?
Ali

Câu trả lời:


94

Ưu điểm duy nhất mà tôi có thể thấy là khi trình lặp trình tự trả về một tham chiếu proxy và bạn cần phải thao tác trên tham chiếu đó theo cách khác không. Ví dụ, hãy xem xét:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto& e : v)
        e = true;
}

Điều này không biên dịch vì rvalue được vector<bool>::referencetrả về từ iteratorsẽ không liên kết với một tham chiếu lvalue không const. Nhưng điều này sẽ hoạt động:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto&& e : v)
        e = true;
}

Tất cả những gì đang được nói, tôi sẽ không viết mã theo cách này trừ khi bạn biết bạn cần phải đáp ứng một trường hợp sử dụng như vậy. Tức là tôi sẽ không làm được điều này cách nhưng không vì nó không gây ra mọi người tự hỏi những gì bạn đang lên đến. Và nếu tôi đã làm điều đó, sẽ không có hại gì nếu bao gồm một bình luận tại sao:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    // using auto&& so that I can handle the rvalue reference
    //   returned for the vector<bool> case
    for (auto&& e : v)
        e = true;
}

Biên tập

Trường hợp cuối cùng này của tôi thực sự nên là một khuôn mẫu có ý nghĩa. Nếu bạn biết vòng lặp luôn xử lý một tham chiếu proxy thì autocũng sẽ hoạt động auto&&. Nhưng khi vòng lặp đôi khi xử lý các tham chiếu không phải proxy và đôi khi là tham chiếu proxy, thì tôi nghĩ auto&&sẽ trở thành giải pháp được lựa chọn.


3
Mặt khác, không có bất lợi rõ ràng , phải không? (Ngoài khả năng gây nhầm lẫn con người, mà tôi không nghĩ là nhiều điều đáng nói, cá nhân.)
ildjarn

7
Một thuật ngữ khác để viết mã gây nhầm lẫn không cần thiết cho mọi người là: viết mã hỗn hợp. Tốt nhất là làm cho mã của bạn càng đơn giản càng tốt, nhưng không đơn giản hơn. Điều này sẽ giúp giảm số lượng lỗi. Điều đó đang được nói, khi && trở nên quen thuộc hơn, thì có lẽ 5 năm nữa mọi người sẽ mong đợi một thành ngữ && auto (giả sử nó thực sự không gây hại). Tôi không biết nếu điều đó sẽ xảy ra hay không. Nhưng đơn giản là trong mắt người xem, và nếu bạn đang viết không chỉ cho bản thân, hãy tính đến độc giả của bạn.
Howard Hinnant

7
Tôi thích const auto&khi tôi muốn trình biên dịch giúp tôi kiểm tra rằng tôi không vô tình sửa đổi các phần tử trong chuỗi.
Howard Hinnant

31
Cá nhân tôi thích sử dụng auto&&trong mã chung, nơi tôi cần sửa đổi các phần tử của chuỗi. Nếu không, tôi sẽ tiếp tục auto const&.
Xeo

7
@Xeo: +1 Chính vì những người đam mê như bạn, không ngừng thử nghiệm và thúc đẩy những cách làm tốt hơn, mà C ++ tiếp tục phát triển. Cảm ơn bạn. :-)
Howard Hinnant 29/10/12

26

Sử dụng auto&&hoặc tham chiếu phổ quát với for-loop dựa trên phạm vi có lợi thế là bạn nắm bắt được những gì bạn nhận được. Đối với hầu hết các loại trình vòng lặp, bạn có thể nhận được một T&hoặc một T const&cho một số loại T. Trường hợp thú vị là khi tham chiếu một trình vòng lặp mang lại giá trị tạm thời: C ++ 2011 có các yêu cầu thoải mái và trình vòng lặp không nhất thiết phải mang lại giá trị. Việc sử dụng các tham chiếu chung phù hợp với chuyển tiếp đối số trong std::for_each():

template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
    for (; it != end; ++it) {
        f(*it); // <---------------------- here
    }
    return f;
}

Đối tượng chức năng fcó thể điều trị T&, T const&Tkhác nhau. Tại sao phần thân của for-loop dựa trên phạm vi phải khác? Tất nhiên, để thực sự tận dụng lợi thế của việc suy ra loại bằng cách sử dụng các tham chiếu phổ quát, bạn cần chuyển chúng tương ứng:

for (auto&& x: range) {
    f(std::forward<decltype(x)>(x));
}

Tất nhiên, việc sử dụng std::forward()có nghĩa là bạn chấp nhận mọi giá trị trả về sẽ được chuyển từ đó. Liệu các đối tượng như thế này có ý nghĩa gì trong mã không phải mẫu hay không mà tôi chưa biết (?). Tôi có thể tưởng tượng rằng việc sử dụng các tham chiếu phổ quát có thể cung cấp thêm thông tin cho trình biên dịch để thực hiện Điều đúng. Trong mã mẫu, nó không đưa ra bất kỳ quyết định nào về những gì sẽ xảy ra với các đối tượng.


9

Tôi hầu như luôn luôn sử dụng auto&&. Tại sao lại bị cắn bởi một chiếc ốp cạnh khi bạn không cần phải làm vậy? Nó cũng ngắn hơn để nhập, và tôi chỉ đơn giản là thấy nó ... trong suốt hơn. Khi bạn sử dụng auto&& x, sau đó bạn biết xchính xác *it, mọi lúc.


29
Vấn đề của tôi là bạn đang đưa lên const-ness với auto&&nếu const auto&đủ. Câu hỏi yêu cầu các trường hợp góc mà tôi có thể bị cắn. Những trường hợp góc mà Dietmar hay Howard chưa đề cập đến là gì?
Ali

2
Đây là một cách để có được cắn nếu bạn làm sử dụng auto&&. Nếu kiểu bạn đang nắm bắt nên được di chuyển trong phần nội dung của vòng lặp (ví dụ) và được thay đổi vào một ngày sau đó để nó chuyển thành một const&kiểu, mã của bạn sẽ im lặng tiếp tục hoạt động, nhưng các bước di chuyển của bạn sẽ được sao chép. Mã này sẽ rất lừa đảo. Tuy nhiên, nếu bạn định rõ loại như một tài liệu tham khảo r có giá trị, bất cứ ai thay đổi container loại sẽ nhận được một lỗi biên dịch vì bạn thực sự thực sự muốn các đối tượng di chuyển và không được sao chép ...
cyberbisson

@cyberbisson Tôi vừa xem qua điều này: làm cách nào để thực thi một tham chiếu rvalue mà không chỉ định rõ ràng loại? Một cái gì đó như thế for (std::decay_t<decltype(*v.begin())>&& e: v)nào? Tôi đoán có một cách tốt hơn ...
Jerry Ma

@JerryMa Nó phụ thuộc vào những gì bạn làm biết về loại. Như đã đề cập ở trên, auto&&sẽ cung cấp cho bạn một "tham chiếu phổ quát", vì vậy bất cứ điều gì khác ngoài điều đó sẽ giúp bạn có được một loại cụ thể hơn. Nếu vđược giả định là a vector, bạn có thể làm decltype(v)::value_type&&, đó là những gì tôi cho rằng bạn muốn bằng cách lấy kết quả của operator*một kiểu trình lặp. Bạn cũng có thể làm decltype(begin(v))::value_type&&để kiểm tra các loại của trình lặp thay vì vùng chứa. Nếu chúng ta có quá ít hiểu biết về các loại, tuy nhiên, chúng ta có thể xem xét nó một chút rõ ràng hơn để chỉ cần đi với auto&&...
cyberbisson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.