基于SFINAE实现随模板类型返回对应字符编码字面量的函数报错求助
基于SFINAE实现随模板类型返回对应字符编码字面量的函数报错求助
兄弟,我看了你的代码,问题出在SFINAE的写法错误上,咱们一步步捋清楚问题,再给你几个更优雅的解决方案:
首先分析报错原因
你写的ConvString模板函数,把std::enable_if_t放在了模板参数的类型位置,比如:
template <typename U = T, typename std::enable_if_t<std::is_same_v<U, wchar_t>, wchar_t>> const std::basic_string_view<U> ConvString(...)
这相当于声明了一个匿名的非类型模板参数,要求调用时必须传递一个wchar_t类型的值,但你调用时根本没传这个参数,编译器自然找不到匹配的函数实例,所以报了"no instance of function template matches the argument list"错误。
修正SFINAE写法的方案
把enable_if_t作为模板参数的默认值,而不是类型,这样编译器就能自动根据模板类型T筛选匹配的函数版本:
template <typename T> class CCodeWriter { public: CCodeWriter() {}; ~CCodeWriter() {}; private: // 针对wchar_t的版本:用enable_if做模板参数约束,默认值0 template <typename U = T, std::enable_if_t<std::is_same_v<U, wchar_t>, int> = 0> const std::basic_string_view<U> ConvString(const std::string_view, const std::wstring_view _WstrView) { return _WstrView; } // 针对char的版本 template <typename U = T, std::enable_if_t<std::is_same_v<U, char>, int> = 0> const std::basic_string_view<U> ConvString(const std::string_view _AstrView, const std::wstring_view) { return _AstrView; } #define CONV_STRING(str) ConvString(str, L##str) private: int m_iIndentation; std::basic_ofstream<T> m_ofs; public: HRESULT Open(stdfs::path const& _path) { return S_OK; } bool IsOpen() const { return m_ofs.is_open(); } void WriteCode(const std::basic_string_view<T> _strCode) { // 现在这两行都能正常编译了 m_ofs << CONV_STRING("\t"); m_ofs << ConvString("\t", L"\t"); } };
这样修改后,编译器会根据U=T的类型,自动选择符合enable_if条件的函数版本,不需要额外传递模板参数。
更优雅的替代方案(不用手动传双字面量)
如果你不想每次都写str和L##str两个字面量,C++17及以上可以用if constexpr来简化,完全不需要SFINAE:
方案1:用constexpr成员函数+宏
在类里加一个constexpr成员函数,配合宏直接生成对应编码的字面量:
template <typename T> class CCodeWriter { public: CCodeWriter() {}; ~CCodeWriter() {}; private: constexpr std::basic_string_view<T> get_literal(const char* str) const noexcept { if constexpr (std::is_same_v<T, char>) { return str; } else if constexpr (std::is_same_v<T, wchar_t>) { return L##str; } } #define LITERAL(str) get_literal(str) private: int m_iIndentation; std::basic_ofstream<T> m_ofs; public: HRESULT Open(stdfs::path const& _path) { return S_OK; } bool IsOpen() const { return m_ofs.is_open(); } void WriteCode(const std::basic_string_view<T> _strCode) { // 直接用宏调用,不用写双字面量 m_ofs << LITERAL("\t"); m_ofs << LITERAL("Hello World"); } };
方案2:用模板变量(适合固定常用字面量)
如果你的常用字面量是固定的(比如\t、\n),可以直接特化模板变量,代码更简洁:
// 类外定义模板变量 template <typename CharT> constexpr std::basic_string_view<CharT> tab_literal; template <> constexpr std::basic_string_view<char> tab_literal<char> = "\t"; template <> constexpr std::basic_string_view<wchar_t> tab_literal<wchar_t> = L"\t"; // 类内使用 template <typename T> class CCodeWriter { public: CCodeWriter() {}; ~CCodeWriter() {}; private: int m_iIndentation; std::basic_ofstream<T> m_ofs; public: HRESULT Open(stdfs::path const& _path) { return S_OK; } bool IsOpen() const { return m_ofs.is_open(); } void WriteCode(const std::basic_string_view<T> _strCode) { // 直接用模板变量,完全不用宏或函数 m_ofs << tab_literal<T>; } };
最后验证
你原来的main函数:
int main() { CCodeWriter<char> writer; writer.WriteCode("HI"); return 0; }
用上面任意一种方案修改后,都能正常编译运行,当模板参数是char时输出窄字符字面量,是wchar_t时输出宽字符字面量,完全符合你的预期。
备注:内容来源于stack exchange,提问作者Tee Mo
相关产品推荐
相关产品推荐

