2013-08-27 40 views
8
struct Foo { 
    void setBar(bool bar_) { bar = bar_; } 
    bool bar; 
}; 

int main() { 
    Foo f; 
    f.setBar("true"); 
} 

Mã trên biên dịch thành công do chuyển đổi loại, mặc dù mảng char được chuyển qua nơi bool được mong đợi.Làm thế nào để ngăn chặn chuyển đổi ngầm từ mảng char sang bool

Có thể khiến mã này không biên dịch được không? (C++ 03 giải pháp được ưu tiên, vì trình biên dịch tại nơi làm việc của tôi là cũ.)

Tôi đã xem các câu hỏi liên quan sau đây trên StackOverflow, nhưng chúng không giải quyết được vấn đề này. Preventing implicit conversion in C++, Why does the compiler choose bool over string for implicit typecast of L""?

+5

Bạn không ** chuyển một chuỗi 'std ::'. Bạn đang truyền một 'const char [5]'. Nó phân rã thành 'const char *', được chuyển thành 'bool'. – juanchopanza

+0

@juanchopanza Có, xin lỗi tôi đã nhận ra rằng sau khi đăng bài. Tôi đã cập nhật câu hỏi ngay bây giờ. –

Trả lời

9

Bạn có thể khai báo một hàm mang theo const char* và không cung cấp một định nghĩa:

void setBar(const char*); 

Điều này sẽ làm cho nó thất bại vào thời điểm liên kết. Bạn vẫn còn tất cả sẽ chuyển đổi ngầm khác, mặc dù - từ bất kỳ con trỏ đến bool, không thể thiếu để bool, phao nổi để bool ...

Một tùy chọn khác:

struct Foo { 
    void setBar(bool bar_) {} 
private: 
    template<typename T> 
    void setBar(T bar) {} 
}; 

Bằng cách này bạn sẽ nhận được một lỗi về nó là riêng tư nếu bạn gọi nó với bất cứ điều gì khác hơn bool.

+1

điều gì về 'riêng tư'? chúng ta có thể sử dụng nó không? –

+0

@Afriza, chắc chắn, nó sẽ chỉ thất bại với thông báo lỗi khác nhau. – jrok

+1

Thất bại tại thời gian liên kết không phải là tốt đẹp như là một thất bại tại thời gian biên dịch. Do đó riêng tư: void setBar (const void *); không có định nghĩa. Ngoài ra tôi recomment không sử dụng một mẫu (tưởng tượng bạn có một lớp wrapper xung quanh một bool). –

7

Một lựa chọn sẽ được làm setBar một mẫu, và cho phép nó chỉ làm việc với bool:

#include <type_traits> 

struct Foo 
{ 
    template <typename T> 
    void setBar(T bar_) 
    { 
    static_assert(std::is_same<bool,T>::value, "not bool"); 
    bar = bar_;   
    } 
    bool bar; 
}; 

int main() { 
    Foo f; 
    f.setBar(true); // OK 
    f.setBar("true"); // Error 
    f.setBar(1);  // Error 
} 

Ngoài ra, bạn có thể sử dụng SFINAE với std::enable_if để tác dụng tương tự, mặc dù cảnh báo trình biên dịch có thể ít dễ đọc:

struct Foo 
{ 
    template<class T , 
      class = typename std::enable_if<std::is_same<bool,T>::value>::type > 
    void setBar(T bar_) 
    { 
     bar = bar_; 
    } 
    bool bar; 
}; 
+1

Điều này, và bạn có thể sử dụng BOOST_STATIC_ASSERT cho giải pháp C++ 03. – jrok

+0

Tôi cũng sẽ đề cập đến 'std :: enable_if'. – lapk

+1

@PetrBudnik Ý tưởng hay, tôi đã thêm một ví dụ. – juanchopanza

5

có một thành ngữ phổ biến mà cả hai tránh được vấn đề này và cung cấp lợi thế khác. Thay vì sử dụng bool, bạn có thể tạo loại tùy chỉnh mô tả rõ hơn trạng thái mà nó đại diện.

Loại bool chỉ đại diện cho giá trị chung của true hoặc false, trong khi sử dụng thực tế bạn đang quá tải các trạng thái này có ý nghĩa cụ thể hơn. Dưới đây là ví dụ sử dụng enum để xác định loại mới:

enum Bar { ok, foobar }; 

struct Foo { 
    void setBar(Bar bar_) { bar = bar_; } 
    Bar bar; 
}; 

int main() { 
    Foo f; 
    f.setBar(foobar); // ok 
    f.setBar("true"); // error 
} 

Điều này vẫn cho phép chuyển đổi ẩn từ bất kỳ số học hoặc loại thả nổi nào. Để tránh điều này, bạn có thể sử dụng số điện thoại enum class của C++ 11 hoặc cuộn số bool được nhập mạnh của bạn như sau:

template<class Tag> 
struct Bool { bool value; }; 

typedef Bool<struct BarTag> Bar; 
const Bar Ok = { false }; 
const Bar FooBar = { true }; 
Các vấn đề liên quan