Tôi có thể trả lại một đường ống tạm thời cho một hoạt động phạm vi không?


9

Giả sử tôi có một generate_my_rangelớp mô hình một range(cụ thể là regular). Sau đó, mã sau đây là chính xác:

auto generate_my_range(int some_param) {    
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  return my_custom_rng_gen(some_param) | ranges::views::transform(my_transform_op);
}
auto cells = generate_my_range(10) | ranges::to<std::vector>;

Được my_custom_rng_gen(some_param)lấy bởi giá trị bởi toán tử đường ống (đầu tiên), hoặc tôi có tham chiếu lơ lửng một khi tôi rời khỏi generate_my_rangephạm vi không?

Nó sẽ giống với cuộc gọi chức năng ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)?

Nó có đúng không nếu tôi sử dụng tài liệu tham khảo lvalue? ví dụ:

auto generate_my_range(int some_param) {
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  auto tmp_ref = my_custom_rng_gen(some_param);
  return tmp_ref | ranges::views::transform(my_transform_op);
}

Nếu phạm vi được lấy bởi các giá trị cho các hoạt động này, thì tôi phải làm gì nếu tôi chuyển một tham chiếu giá trị cho một container? Tôi có nên sử dụng một ranges::views::all(my_container)mô hình?


My_custom_rng_gen (some_param) đã bị ràng buộc chưa? Bạn có nghĩa là một cái gì đó như godbolt.org/z/aTF8RN mà không mất (5)?
Porsche9II

@ Porsche9II Có, đây là một phạm vi giới hạn. Hãy nói rằng đó là một container
Bé tiếp

Câu trả lời:


4

Trong thư viện phạm vi có hai loại hoạt động:

  • Các khung nhìn lười biếng và yêu cầu vùng chứa bên dưới tồn tại.
  • kết quả là những hành động háo hức và tạo ra các thùng chứa mới (hoặc sửa đổi những cái hiện có)

Lượt xem rất nhẹ. Bạn vượt qua chúng theo giá trị và yêu cầu các thùng chứa bên dưới vẫn còn hiệu lực và không thay đổi.

Từ tài liệu phạm vi-v3

Một khung nhìn là một trình bao bọc nhẹ thể hiện một khung nhìn của một chuỗi các phần tử bên dưới theo một cách tùy chỉnh nào đó mà không làm thay đổi hoặc sao chép nó. Lượt xem rẻ để tạo và sao chép và có ngữ nghĩa tham chiếu không sở hữu.

và:

Bất kỳ hoạt động nào trên phạm vi cơ bản làm mất hiệu lực các trình vòng lặp hoặc các câu lệnh của nó cũng sẽ làm mất hiệu lực bất kỳ chế độ xem nào đề cập đến bất kỳ phần nào của phạm vi đó.

Sự phá hủy của container bên dưới rõ ràng làm mất hiệu lực tất cả các trình vòng lặp với nó.

Trong mã của bạn, bạn đặc biệt sử dụng các khung nhìn - Bạn sử dụng ranges::views::transform. Các ống chỉ đơn thuần là một đường cú pháp để làm cho nó dễ dàng để viết theo cách của nó. Bạn nên nhìn vào thứ cuối cùng trong đường ống để xem những gì bạn sản xuất - trong trường hợp của bạn, đó là một góc nhìn.

Nếu không có người vận hành đường ống, nó có thể trông giống như thế này:

ranges::views::transform(my_custom_rng_gen(some_param), my_transform_op)

nếu có nhiều biến đổi được kết nối theo cách đó bạn có thể thấy nó sẽ xấu đến mức nào.

Do đó, nếu my_custom_rng_gentạo ra một loại container nào đó, mà bạn biến đổi và sau đó quay trở lại, container đó sẽ bị phá hủy và bạn có các tham chiếu lơ lửng từ quan điểm của mình. Nếu my_custom_rng_genlà một góc nhìn khác về một container sống bên ngoài các phạm vi này, mọi thứ đều ổn.

Tuy nhiên, trình biên dịch sẽ có thể nhận ra rằng bạn đang áp dụng chế độ xem trên một bộ chứa tạm thời và đánh bạn với một lỗi biên dịch.

Nếu bạn muốn hàm của mình trả về một phạm vi dưới dạng một thùng chứa, bạn cần phải "cụ thể hóa" kết quả. Đối với điều đó, sử dụng ranges::totoán tử trong hàm.


Cập nhật: Để dễ hiểu hơn về nhận xét của bạn "tài liệu nói rằng việc soạn phạm vi / đường ống lấy và lưu trữ chế độ xem ở đâu?"

Ống chỉ đơn thuần là một đường cú pháp để kết nối mọi thứ trong một biểu thức dễ đọc. Tùy thuộc vào cách nó được sử dụng, nó có thể hoặc không thể trả về một khung nhìn. Nó phụ thuộc vào đối số bên phải. Trong trường hợp của bạn, đó là:

`<some range> | ranges::views::transform(...)`

Vì vậy, biểu thức trả về bất cứ điều gì views::transformtrả về.

Bây giờ, bằng cách đọc tài liệu của biến đổi:

Dưới đây là danh sách các tổ hợp phạm vi lười biếng hoặc các chế độ xem mà Range-v3 cung cấp và giới thiệu về cách sử dụng từng tổ hợp.

[...]

views::transform

Đưa ra một phạm vi nguồn và một hàm unary, trả về một phạm vi mới trong đó mỗi phần tử kết quả là kết quả của việc áp dụng hàm unary cho một phần tử nguồn.

Vì vậy, nó trả về một phạm vi, nhưng vì nó là một toán tử lười biếng, nên phạm vi đó nó trả về một khung nhìn, với tất cả các ngữ nghĩa của nó.


Đồng ý. Điều hơi bí ẩn đối với tôi vẫn là nó hoạt động như thế nào khi tôi chuyển một container vào đường ống (tức là đối tượng phạm vi được tạo bởi bố cục). Nó cần phải lưu trữ một cái nhìn của container bằng cách nào đó. Nó được thực hiện với ranges::views::all(my_container)? Và nếu một khung nhìn được truyền vào đường ống thì sao? Nó nhận ra nó được thông qua một container hoặc một khung nhìn? Có cần không? Làm sao?
Bé tiếp

"Trình biên dịch sẽ có thể nhận ra rằng bạn đang áp dụng chế độ xem trên một bộ chứa tạm thời và đánh bạn với một lỗi biên dịch" Đó là những gì tôi cũng nghĩ: nếu tôi làm điều gì đó ngu ngốc, điều đó có nghĩa là một hợp đồng ở loại (là bên trái giá trị) không được thực hiện. Những thứ như thế được thực hiện bởi phạm vi-v3. Nhưng trong trường hợp này không có vấn đề gì. Nó biên dịch VÀ chạy. Vì vậy, có thể có hành vi không xác định, nhưng nó không hiển thị.
Bé tiếp

Để chắc chắn nếu mã của bạn chạy chính xác do tai nạn hoặc nếu mọi thứ đều ổn tôi sẽ cần phải xem nội dung của my_custom_rng_gen. Làm thế nào chính xác đường ống và transformtương tác dưới mui xe không quan trọng. Toàn bộ biểu thức lấy một phạm vi làm đối số (vùng chứa hoặc dạng xem cho vùng chứa nào đó) và trả về một dạng xem khác cho vùng chứa đó. Giá trị trả về sẽ không bao giờ sở hữu container, bởi vì nó là một khung nhìn.
CygnusX1

1

Lấy từ tài liệu phạm vi-v3 :

Lượt xem [...] có ngữ nghĩa tham chiếu không sở hữu.

Có một đối tượng phạm vi duy nhất cho phép các đường ống hoạt động. Trong một đường ống, một phạm vi được điều chỉnh một cách lười biếng hoặc háo hức theo một cách nào đó, với kết quả ngay lập tức có sẵn để thích ứng hoặc đột biến hơn nữa. Thích nghi lười biếng được xử lý bởi các quan điểm, và đột biến háo hức được xử lý bằng hành động.

// taken directly from the the ranges documentation
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){ return i % 2 == 1; })
              | views::transform([](int i){ return std::to_string(i); });
// rng == {"2","4","6","8","10"};

Trong đoạn mã trên, rng chỉ cần lưu trữ một tham chiếu đến dữ liệu cơ bản và các hàm lọc và biến đổi. Không có công việc được thực hiện cho đến khi rng được lặp đi lặp lại.

Vì bạn nói rằng phạm vi tạm thời có thể được coi là một container, hàm của bạn trả về một tham chiếu lơ lửng.

Nói cách khác, bạn cần đảm bảo rằng phạm vi bên dưới tồn tại lâu hơn tầm nhìn hoặc bạn đang gặp rắc rối.


Đúng, các khung nhìn là không sở hữu, nhưng tài liệu nói rằng việc soạn phạm vi / đường ống lấy và lưu trữ một khung nhìn ở đâu? Có thể (và tôi nghĩ, một điều tốt) có chính sách sau: lưu trữ theo giá trị nếu phạm vi được đưa ra bởi một tham chiếu giá trị.
Bé tiếp

1
@ Bé bôier Tôi đã thêm một chút từ tài liệu phạm vi. Nhưng quan điểm thực sự là: Một quan điểm là không sở hữu . Nó không quan tâm cho dù bạn đưa nó một giá trị.
Rumburak
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.