这是一个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
?为什么foo
对char
的显式实例化(使用显式模板参数或带有演绎)是否足以解决此问题?
这似乎意味着我们不得不在公开的API中使用模板和basic_string
,而是将其用作实现细节,但会为用户提供std::string
的重载,{{ 1}}等等,这是一种耻辱。
谢谢!
答案 0 :(得分:5)
对于bar("abc")
,存在从char const[4]
到std::string
的隐式转换。 foo
与bar
的不同之处在于它实际上不是函数,而是函数模板。需要知道它的模板参数才能构建正确的函数。
对foo
的第一次调用显式提供了模板参数,因此它构建了一个如下所示的函数:
void foo(const std::basic_string<char, std::char_traits<char>, std::allocator<char>>&);
隐式转换开始了,一切都很好。
第三个调用不提供模板参数,因此编译器必须从类型CharT
中找出Traits
,Allocator
和char const[4]
的类型。这种类型不携带该信息,因此扣除失败,重载解析无法找到正确的函数。
答案 1 :(得分:4)
这适合你吗?
template <typename StringT>
void foo(const StringT& the_string)
{
typedef decltype(the_string[0]) CharT;
// do the work
}
这可以将StringT
推断为std::string
,std::wstring
,const 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::string
和std::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); }