Chuyển đổi ngầm định không được phép khi trở lại


21
#include <optional>

bool f() {
  std::optional<int> opt;
  return opt;
}

Không biên dịch: 'return': cannot convert from 'std::optional<int>' to 'bool'

Tư vấn tham khảo Tôi đã có thể nghĩ để tìm một lời giải thích, nhưng tôi đọc nó vì nó sẽ ổn.

Chuyển đổi ngầm định được thực hiện bất cứ khi nào một biểu thức của một loại T1 được sử dụng trong ngữ cảnh không chấp nhận loại đó, nhưng chấp nhận một số loại T2 khác; đặc biệt:

  • khi biểu thức được sử dụng làm đối số khi gọi một hàm được khai báo với T2 là tham số;
  • khi biểu thức được sử dụng như một toán hạng với toán tử mong đợi T2;
  • khi khởi tạo một đối tượng mới thuộc loại T2, bao gồm câu lệnh return trong hàm trả về T2;
  • khi biểu thức được sử dụng trong câu lệnh chuyển đổi (T2 là loại tích phân);
  • khi biểu thức được sử dụng trong câu lệnh if hoặc vòng lặp (T2 là bool).

7
" Implicit chuyển đổi được thực hiện" , nhưng operator bool()trong std::optionalexplicit.
Jarod42

Câu trả lời:


22

std::optionalkhông có bất kỳ cơ sở nào để ngầm chuyển đổi sang bool. (Cho phép chuyển đổi ngầm định boolthường được coi là một ý tưởng tồi, vì boollà một loại không thể tách rời nên một cái gì đó giống như int i = optsẽ biên dịch và làm hoàn toàn sai.)

std::optional không có "chuyển đổi theo ngữ cảnh" thành bool, định nghĩa trông giống như một toán tử cast : explicit operator bool(). Điều này không thể được sử dụng để chuyển đổi ngầm; nó chỉ áp dụng trong một số tình huống cụ thể trong đó "bối cảnh" dự kiến ​​là một tình huống boolean, giống như điều kiện của câu lệnh if.

Những gì bạn muốn là opt.has_value().


4

Từ tài liệu C ++ :

Khi một đối tượng loại tùy chọn <T> được chuyển đổi theo ngữ cảnh thành bool, chuyển đổi trả về true nếu đối tượng chứa giá trị và false nếu nó không chứa giá trị.

Đọc về chuyển đổi theo ngữ cảnh ở đây :

Trong các bối cảnh sau đây, loại bool được mong đợi và chuyển đổi ngầm được thực hiện nếu khai báo bool t (e); được định dạng tốt (nghĩa là một hàm chuyển đổi rõ ràng, chẳng hạn như toán tử T :: bool () const; được xem xét). Biểu thức như vậy e được cho là được chuyển đổi theo ngữ cảnh thành bool.

  • biểu thức kiểm soát của if, while, for;
  • các toán hạng của các toán tử logic tích hợp!, && và ||;
  • toán hạng đầu tiên của toán tử điều kiện?:;
  • vị ngữ trong khai báo static_assert;
  • biểu thức trong một specifier noexcept;
  • biểu thức trong một specifier rõ ràng;

Bạn có thể thực hiện hack sau:

bool f() {
    std::optional<int> opt;
    return opt || false;
}

bởi vì chuyển đổi theo ngữ cảnh xảy ra trong trường hợp các toán tử logic tích hợp, nhưng chuyển đổi theo ngữ cảnh không bao gồm các returncâu lệnh và std::optionalbản thân nó không có chuyển đổi ngầm định thành bool.

Vì vậy, nó là tốt nhất để sử dụng std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}

những gì về return {opt}? hoặcreturn bool{opt};
darune

3
@darune return {opt};sẽ không hoạt động nhưng return static_cast<bool>(opt);hoặc return bool{opt};sẽ làm việc. Tuy nhiên, nên sử dụng has_valuechức năng thành viên vì nó thực sự cho thấy ý định rõ ràng về những gì bạn muốn làm
NutCracker

Hoặc nổi tiếng return !!pot;Hack ( has_valuelà tốt hơn)
LF

1

Đó là vì sự bao phủ ngầm của std :: tùy chọn cho bool không được hỗ trợ: https://en.cppreference.com/w/cpp/utility/optional/operator_bool

constexpr toán tử tường minh bool () const noexcept;

Bạn phải chuyển đổi rõ ràng sang bool như bool(opt)hoặc đơn giản là sử dụng opt.has_value()thay thế.


bool {opt} cũng hoạt động tốt và nên được ưu tiên hơn bool (opt)
darune

1

Đây không thực sự là về chuyển đổi ngầm, đây là về kiểu khởi tạo.

Những gì tùy chọn có là một chức năng chuyển đổi rõ ràng, tức là

explicit operator bool() const; 

Từ N4849 [class.conv.fct] / p2

Hàm chuyển đổi có thể rõ ràng (9.2.2), trong trường hợp đó, nó chỉ được coi là chuyển đổi do người dùng xác định để khởi tạo trực tiếp.

Điều trên có nghĩa là những trường hợp này sẽ sử dụng chức năng chuyển đổi: [dcl.init] / p16

Việc khởi tạo xảy ra (16.1) - đối với trình khởi tạo là danh sách biểu thức được ngoặc đơn hoặc danh sách khởi tạo, (16.2) - cho trình khởi tạo mới (7.6.2.7), (16.3) - trong biểu thức static_cast ( 7.6.1.8), (16.4) - trong chuyển đổi loại ký hiệu chức năng (7.6.1.3) và (16.5) - ở dạng danh sách khởi tạo của một điều kiện được gọi là khởi tạo trực tiếp.

Tuy nhiên, những trường hợp này sẽ không sử dụng chức năng chuyển đổi: [dcl.init] / p15

Việc khởi tạo xảy ra ở dạng = của một công cụ khởi tạo hoặc điều kiện bằng (brace-hoặc-bằng) (8,5), cũng như trong truyền đối số, trả về hàm, ném ngoại lệ (14.2), xử lý ngoại lệ (14.4) và khởi tạo thành viên (9.4.1), được gọi là khởi tạo bản sao.

Ví dụ trong câu hỏi thuộc trường hợp khởi tạo sao chép và không sử dụng chức năng chuyển đổi tùy chọn.

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.