basic_string <chart>与CharT * </chart>

时间:2014-10-27 15:45:12

标签: c++ templates c++11 type-deduction stdstring

这是一个FAQ,但我找不到令人满意的答案。在我的项目中,我们支持std::string,现在还必须支持宽字符串。所以我们想要转移到basic_string,但是,事情就会停止工作,并且需要明确地拼写参数:

#include <string>

template <typename CharT, typename Traits, typename Allocator>
void
foo(const std::basic_string<CharT, Traits, Allocator>&)
{}

template void foo(const std::string&);

// template void
// foo<char, std::char_traits<char>, std::allocator<char>>(const std::string&);

void bar(const std::string& s)
{}

int main()
{
  bar("abc");
  foo<char, std::char_traits<char>, std::allocator<char>>("def");
  foo("def");
}

好吧,由于众所周知的原因,它失败了:

clang++-mp-3.5 -Wall -std=c++11 foo.cc 
foo.cc:20:3: error: no matching function for call to 'foo'
  foo("def");
  ^~~
foo.cc:5:1: note: candidate template ignored: could not match
      'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>'
      against 'char const[4]'
foo(const std::basic_string<CharT, Traits, Allocator>&)
^

我不理解为什么它适用于bar?为什么foochar的显式实例化(使用显式模板参数或带有演绎)是否足以解决此问题?

这似乎意味着我们不得不在公开的API中使用模板和basic_string,而是将其用作实现细节,但会为用户提供std::string的重载,{{ 1}}等等,这是一种耻辱。

谢谢!

3 个答案:

答案 0 :(得分:5)

对于bar("abc"),存在从char const[4]std::string的隐式转换。 foobar的不同之处在于它实际上不是函数,而是函数模板。需要知道它的模板参数才能构建正确的函数。

foo的第一次调用显式提供了模板参数,因此它构建了一个如下所示的函数:

void foo(const std::basic_string<char, std::char_traits<char>, std::allocator<char>>&);

隐式转换开始了,一切都很好。

第三个调用不提供模板参数,因此编译器必须从类型CharT中找出TraitsAllocatorchar const[4]的类型。这种类型不携带该信息,因此扣除失败,重载解析无法找到正确的函数。

答案 1 :(得分:4)

这适合你吗?

template <typename StringT>
void foo(const StringT& the_string)
{
    typedef decltype(the_string[0]) CharT;
    // do the work
}

这可以将StringT推断为std::stringstd::wstringconst char[N]const wchar_t[N]std::vector<char>等等。如果您想隐式转换预先将C风格的字符串转换为std :: string,这样就可以使用所有STL集合通用的成员函数,添加一个捕获数组的转发重载:

template <typename CharT, size_t N>
void foo(const CharT (&char_array_or_literal)[N])
{
    foo(std::basic_string<CharT>(char_array_or_literal));
}

也许是角色指针的另一个:

template <typename CharT>
void foo(const CharT* char_ptr)
{
    foo(std::basic_string<CharT>(char_ptr));
}

另一方面,如果您需要basic_string的所有功能,则应使用广泛模板进行转发:

template <typename CharT, typename Traits, typename Allocator>
void foo(const std::basic_string<CharT, Traits, Allocator>& the_string)
{
    // the real work is done here
}

template <typename StringLikeT>
void foo(const StringLikeT& the_string_like_thing)
{
    typedef decltype(the_string_like_thing[0]) CharT;
    // this turns string literals, arrays, pointers, vectors, std::array, all into basic_string
    foo(basic_string<CharT>(&the_string_like_thing[0]));
}

答案 2 :(得分:0)

最佳解决方法似乎是为委托给函数模板Demo at Coliru)的std::stringstd::wstring提供非模板重载:

template <typename CharT, typename Traits, typename Allocator>
void foo(const std::basic_string<CharT, Traits, Allocator>&)
{}

inline void foo(const std::string& u) { foo<>(u); }
inline void foo(const std::wstring& u) { foo<>(u); }