我有以下两个功能:
void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}
void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}
这两个函数都调用一些C函数,这些函数接受const char *
或const wchar_t *
并分别具有U
或W
后缀。我想创建一个模板函数来处理这两种情况。我尝试了以下尝试:
template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}
但这似乎无法正常工作。如果我打电话:
foo("abc");
这将无法编译。这是为什么?为什么编译器无法将正确的类型T
推导为char
?是否可以创建一个可以处理std :: string和std :: wstring的函数?
答案 0 :(得分:4)
这里的问题是,在foo("abc");
中,"abc"
不是std::string
或std::wstring
,而是const char[N]
。由于它不是std::string
或std::wstring
,因此编译器无法推断出T
应该是什么,并且编译失败。最简单的解决方案是使用现有的资源。将考虑重载,将"abc"
转换为std::string
是一个更好的选择,因此它将调用该版本的函数。
如果需要,可以使用std::string_view
/ std::wstring_view
而不是std::string
/ std::wstring
,因此,如果您向函数传递字符串,则实际上不会分配任何内存文字。这样会将重载更改为
void bar(std::string_view s)
{
someCFunctionU(s.data());
}
void bar(std::wstring_view s)
{
someCFunctionW(s.data());
}
请注意,std::basic_string_view
可以在没有空终止符的情况下构造,因此可以传递std::basic_string_view
来满足C函数所具有的以空终止的c字符串的要求。在这种情况下,代码具有未定义的行为。
答案 1 :(得分:4)
这将无法编译。这是为什么?为什么编译器无法将正确的类型T推导为char?
正如其他人更好地解释的那样,"abc"
是char[4]
,因此可以转换为std::basic_string<char>
但不是std::basic_string<char>
,因此不能推论为T
键入char
作为接受std::basic_string<T>
的模板函数。
是否可以创建一个可以同时处理std :: string和std :: wstring的函数?
是的,有可能;但是您的双重功能超载解决方案出了什么问题?
无论如何,如果您真的想要一个功能并且如果您愿意编写很多工具,那么我想您可以编写如下内容
template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_same_v<T, std::string> )
someCFunctionU(s.c_str());
else if constexpr ( std::is_convertible_v<T, char const *>)
someCFunctionU(s);
else if constexpr ( std::is_same_v<T, std::wstring> )
someCFunctionW(s.c_str());
else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
someCFunctionW(s);
// else exception ?
}
或者,综合性更高但效率更低的
template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_convertible_v<T, std::string> )
someCFunctionU(std::string{s}.c_str());
else if constexpr (std::is_convertible_v<T, std::wstring> )
someCFunctionW(std::wstring{s}.c_str());
// else exception ?
}
因此,您应该可以使用foo()
,std::string
,std::wstring
,char *
,wchar_t *
或{{1} }。
答案 2 :(得分:2)
C ++ 17中的一种解决方法是:
SELECT COUNT(*)
FROM mytable
WHERE StationTo = 'P11004400000'
并避免Justin提到有关非空终止字符串的问题
template <typename T>
void foo(const T &s)
{
std::basic_string_view sv{s}; // Class template argument deduction
if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
someCFunctionU(sv.data());
else
someCFunctionW(sv.data());
}
答案 3 :(得分:1)
是的,存在一种类型,即std::basic_string<char>
,可以从表达式"abc"
进行复制初始化。因此,您可以使用参数void foo(std::basic_string<char>)
调用"abc"
之类的函数。
不,您不能使用参数template <class T> void foo(const std::basic_string<T> &s)
调用函数模板"abc"
。因为为了弄清楚参数是否可以由参数初始化,所以编译器需要首先确定模板参数T
。它将尝试将const std::basic_string<T> &
与const char [4]
进行匹配。它将失败。
之所以失败,是因为模板参数推导规则。实际规则非常复杂。但是在这种情况下,为了在推论期间检查std::basic_string<char>
,编译器将需要寻找适当的“转换构造函数”,即可以用参数"abc"
隐式调用的构造函数,并进行这种查找扣除期间标准不允许。
是的,可以在一个函数模板中处理std :: string和std :: wstring:
void foo_impl(const std::string &) {}
void foo_impl(const std::wstring &) {}
template <class T>
auto foo(T &&t) {
return foo_impl(std::forward<T>(t));
}