假设我们有一个可以将东西写入输出的类
class Writer { public: int write(const std::string& str); int write(const char* str,int len); //... };
我很好,这很灵活,所有这一切,直到我意识到
char* buf = new char[n]; //not terminated with '\0' //load up buf Writer w; w.write(buf); //compiles!
这是一个非常讨厌的bug.
我们可以用一些模板来修改
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"); } //... };
但这种方法存在问题
WriterV2 w; w.write("The templating genius!"); //compile error
我该怎么办?什么是更好的设计?
在有人要求之前,重载为const char(&)[N] does not work.创建一个包装器来实现这一点可能是可行的,但这看起来……有点过分了吗?
编辑添加方法write(char *)并发出错误并不理想.当通过函数和所有这些传递buf时,它可能变成const char *.
解决方法
在C中的重载分辨率期间
ICS (Implicit Conversion Sequences)可以产生令人惊讶的结果,你已经注意到了,也很烦人..
您可以提供所需的必要接口,然后通过利用partial ordering删除不需要的重载,仔细使用模板来处理字符串文字与const char * fiasco.
码:
#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]); }
打印:
int write(const char* str,int len) int write(std::string) int write(string-literal) 13
请注意,上述解决方案继承了“使用无约束转发引用重载函数”的缺点.这意味着过载集中可行函数的参数类型的所有ICS都将被“删除”