loại trả về std :: cặp <auto, auto>


16

Tôi đã chơi xung quanh với autotrong std::pair. Trong đoạn mã dưới đây, hàm fđược cho là trả về một std::pairloại phụ thuộc vào tham số mẫu.

Một ví dụ làm việc:

VÍ DỤ 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

Điều này hoạt động với gcc 9.2, gcc 10.0, clang 9.0 và clang 10.0.

Tiếp theo, tôi muốn viết rõ ràng kiểu trả về như một std::pairlý do rõ ràng:

VÍ DỤ 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

Cả gcc 9.2 / 10.0 và clang 9.0 / 10.0 đều thất bại trong việc biên dịch này.

gcc 9,2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

Từ thông báo lỗi cuối cùng, gcc 9.2 dường như tin rằng đó std::pair<auto, auto>là một int. làm như thế nào để giải thích chuyện này?

gcc 10.0

error: returning initializer list

Lỗi này có thể hiểu được, tuy nhiên, tôi dự kiến ​​hàm tạo std::pairsẽ được gọi, hoặc có thiếu thứ gì ở đây không?

tiếng kêu 9.0 và 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

Ok, clang không thích bất kỳ thứ gì trong số này. Từ thông báo lỗi thứ hai, dường như clang cũng tin rằng kiểu trả về là int.

Cuối cùng, để sửa lỗi thu được bằng cách biên dịch với gcc 10.0, tôi quyết định trả lại một std::paircách rõ ràng:

VÍ DỤ 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

tiếng kêu 9.0 và 10.0

Tương tự như trước đây, nhưng có thêm:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

Ở đây clang vẫn nghĩ rằng chúng tôi đang trở lại int?

gcc 9,2

Giống như trước.

gcc 10.0

Nó hoạt động!

Tôi đoán một số tính năng vẫn phải được thực hiện, hoặc trong một trong các tình huống được mô tả ở trên, có trình biên dịch nào đúng và sai không? Theo tôi, ví dụ 2 nên hoạt động. Hay không nên?

Câu trả lời:


23

Cú pháp:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

Là một phần của các khái niệm ban đầu TS nhưng không được bao gồm trong đề xuất các khái niệm là một phần của C ++ 20. Như vậy, các loại giữ chỗ duy nhất trong C ++ 20 là auto(và các biến thể của chúng như thế nào auto**) decltype(auto)và các giữ chỗ bị ràng buộc ( Concept autovà các biến thể của chúng). Kiểu giữ chỗ lồng nhau này sẽ rất hữu ích, nhưng không phải là một phần của C ++ 20, do đó khai báo hàm không được định dạng.

Bây giờ, gcc cho phép nó vì gcc đã triển khai các khái niệm TS và tôi đoán họ đã quyết định giữ tính năng này. clang không bao giờ thực hiện TS, vì vậy nó không.

Dù bằng cách nào, điều này:

std::pair<auto, auto> f() { return {1, 2}; }

Sẽ luôn luôn bị hình thành. Ý nghĩa của cú pháp là chúng tôi suy ra kiểu trả về và sau đó yêu cầu nó phù hợp pair<T, U>với một số loại TU. Về cơ bản chúng tôi đang cố gắng gọi hàm được phát minh:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

Nhưng bạn không thể suy ra một loại từ {1, 2}- một danh sách chuẩn bị không có một loại. Có lẽ đây là điều cần được khám phá (vì nó dễ hiểu ít nhất là trong trường hợp đơn giản như thế này), nhưng nó chưa bao giờ được cho phép. Vì vậy, từ chối nó là chính xác một trong hai cách.

Cuối cùng:

gcc 9.2 dường như tin rằng đó std::pair<auto, auto>là một int. làm như thế nào để giải thích chuyện này?

Vì một số lý do (có thể là do di sản C của chúng tôi có ẩn int), khi gcc không nhận ra hoặc hiểu một loại, nó chỉ sử dụng intnhư trình giữ chỗ trong các thông báo lỗi. Điều này thật khó hiểu, vì rõ ràng đó là gcc xuất hiện intvà không phải mã nguồn. Nhưng đó là cách nó được.


"Danh sách chuẩn bị không có đối số kiểu" đối với tôi không rõ ràng. std :: cặp <int, int> f () {return {1,2}; } không hoạt động và {1,2} không có loại (nó gọi hàm tạo của std :: cặp <int, int> như tôi hiểu). Có lẽ với <auto, auto>, trình biên dịch không thể suy ra các loại 1 và 2 trong danh sách khởi tạo {1, 2}?
mfnx

@mfnx Không có đối số loại , chỉ không có loại. danh sách init giằng chỉ có thể được sử dụng trong một số trường hợp - như khởi tạo một kiểu đã biết. Nhưng chúng không thể được sử dụng để khấu trừ - bởi vì chúng không có loại. Ngoại trừ auto x = {1, 2};các công trình, nhưng chỉ khi tất cả các loại là như nhau.
Barry

2
Hầu hết các trình biên dịch, thay vì chỉ dừng lại ở lỗi đầu tiên, hãy cố gắng khôi phục từ đó để chúng có thể báo cáo các lỗi bổ sung. Điều này thường có nghĩa là giả định rằng mọi thứ không thể nhìn thấy là một int. Đó không phải intlà một trình giữ chỗ trong các thông báo lỗi; trình biên dịch thực sự nghĩ rằng đó là một int. (Để làm cho điều này rõ ràng hơn, gcc có lẽ nên nói "giả sử int" tại một số điểm.)
Raymond Chen

2
Lưu ý rằng một con đường khả dĩ khác cho tiện ích mở rộng sẽ là cho phép khấu trừ đối số mẫu lớp cho các kiểu trả về, vì std::pair __f{1,2};hoạt động.
Davis Herring

2
@DavisHerring Tôi sẽ không thực sự muốn có std::optional f() { return 4; }công việc.
Barry
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.