使用模板处理字符串和字符串

时间:2018-11-20 18:53:41

标签: c++ templates c++17 template-meta-programming

我有以下两个功能:

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 *并分别具有UW后缀。我想创建一个模板函数来处理这两种情况。我尝试了以下尝试:

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的函数?

4 个答案:

答案 0 :(得分:4)

这里的问题是,在foo("abc");中,"abc"不是std::stringstd::wstring,而是const char[N]。由于它不是std::stringstd::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::stringstd::wstringchar *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));
}