2017-05-06 21 views
6

Giả sử chúng ta có một lớp học mà có thể viết công cụ để sản lượngGiao diện thiết kế: an toàn của chức năng quá tải tham gia chuỗi và char mảng

class Writer 
{ 
public: 
    int write(const std::string& str); 
    int write(const char* str, int len); 
    //... 
}; 

tôi vẫn ổn với điều này, linh hoạt của nó và tất cả những gì, cho đến khi tôi nhận ra

char* buf = new char[n]; //not terminated with '\0' 
//load up buf 
Writer w; 
w.write(buf); //compiles! 

Đó là một lỗi thực sự thực sự là.

Chúng ta có thể sửa đổi một chút với một số khuôn mẫu

class WriterV2 
{ 
public: 
    int write(const std::string& str); 
    int write(const char* str, int len); 
    template<typename... Args> 
    int write(const char*, Args...) 
    { static_assert(sizeof...(Args) < 0, "Incorrect arguments"); } 
    //... 
}; 

Nhưng phương pháp này có vấn đề của nó

WriterV2 w; 
w.write("The templating genius!"); //compile error 

Tôi phải làm gì? Một thiết kế tốt hơn là gì?

Và trước khi có ai đó hỏi, quá tải cho const char (&)[N]does not work. Nó có thể là khả thi để tạo ra một wrapper để làm điều này, nhưng có vẻ như ... overkill?

EDIT Thêm phương thức write(char*) và phát ra lỗi không lý tưởng. Khi vượt qua buf xung quanh thông qua các chức năng và tất cả các chức năng, nó có thể trở thành const char*.

+1

Tại sao bạn không khai báo phương thức để xử lý trường hợp này nhưng không triển khai? int write (char * str); – ifma

+0

Được rồi, nhưng tại sao phải có sự phân biệt giữa hai loại? Ngoài ra, gọi 'w.write (buf);' sẽ _always_ gọi 'int Writer :: write (const std :: string & str);' Nếu bạn muốn nó gọi 'int Writer :: write (const char * str, int len) ; ', sau đó thêm tham số chiều dài vào cuộc gọi của bạn – InternetAussie

+0

Sử dụng std :: vector làm đệm. –

Trả lời

5

ICS (Implicit Conversion Sequences) trong giải quyết tình trạng quá tải trong C++ có thể tạo ra kết quả đáng ngạc nhiên là bạn đã nhận thấy, và cũng khá khó chịu ..

Bạn có thể cung cấp các giao diện cần thiết mà bạn cần, sau đó cẩn thận sử dụng các mẫu để xử lý các chuỗi chữ vs const char* fiasco bằng cách tận dụng lợi thế của partial ordering đến delete quá tải không mong muốn.

Code:

#include <iostream> 
#include <string> 
#include <type_traits> 

class Writer 
{ 
public: 
    int write(std::string&&) { std::cout << "int write(std::string)\n"; return 0; } 
    int write(const std::string&) { std::cout << "int write(const std::string& str)\n"; return 0; } 
    int write(const char*, int){ std::cout << "int write(const char* str, int len)\n"; return 0; } 

    template<std::size_t N = 0, typename = std::enable_if_t<(N > 0)> > 
    int write(const char (&)[N]) { std::cout << "int write(string-literal) " << N << " \n"; return 0; } 


    template<typename T> 
    int write(T&&) = delete; 

}; 

int main(){ 
    char* buf = new char[30]; 
    const char* cbuf = buf; 
    Writer w; 

    //w.write(buf);      //Fails! 
    //w.write(cbuf);     //Fails! 
    w.write(buf, 30);     //Ok! int write(const char*, int); 
    w.write(std::string("Haha"));  //Ok! int write(std::string&&); 
    w.write("This is cool");   //Ok! int write(const char (&)[13]); 
} 

Prints:

int write(const char* str, int len) 
int write(std::string) 
int write(string-literal) 13 

Demo


Lưu ý rằng giải pháp trên được thừa hưởng một bất lợi của "quá tải một chức năng với một không bị giới hạn Forwarding Reference ". Điều này có nghĩa là tất cả ICS đối với loại đối số của các hàm khả thi trong tập quá tải sẽ bị "xóa"

+0

Để hoàn thành, bạn có thể thêm vào đó điều này có nhược điểm của việc vô hiệu hóa * tất cả * chuyển đổi ẩn thành 'std :: string'? –

+0

@PasserBy, đã thêm. Cảm ơn! – WhiZTiM

Các vấn đề liên quan