Basic_string <CharT> vs CharT *

This is a FAQ, but I couldn't find a satisfactory answer. In my project we support std::string

and should now also support wide strings. Therefore, we want to switch to basic_string

, but then everything stops working nicely, and the parameters should be clearly spelled out:

#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");
}

      

OK, this is happening for a known reason:

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>&)
^

      

What I don't understand is why it works for bar

? Why is explicit instantiation foo

for char

(either with explicit template parameters or subtraction) not enough for this problem ?

This seems to mean that instead of using templates and basic_string

in the public API, we will have to use it as an implementation detail, but expose the user with overloads for std::string

, std::wstring

etc. which is a shame.

Thank!

+3


source to share


3 answers


For bar("abc")

there is an implicit conversion from char const[4]

to std::string

. foo

different from the bar

fact that it is not actually a function, but a function template. Its template arguments must be known in order to build a correct function.

The first call foo

explicitly provides template arguments, so it creates a function that looks like this:

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

      



Implicit conversion and everything is fine.

The third call does not provide template arguments, so the compiler must determine the type CharT

, Traits

and Allocator

from the type char const[4]

. This type does not carry this information, so no inference is performed and overload resolution cannot find the correct function.

+5


source


This works for you:

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

      

It can output StringT

as the std::string

, std::wstring

, const char[N]

, const wchar_t[N]

, std::vector<char>

, etc. And if you want implicit conversions of C-style strings to std :: string beforehand, so that you can use member functions common to all STL collections, add an overloading overload that captures arrays:

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

      



And maybe another one for character pointers:

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

      

If, on the other hand, you want all the functionality basic_string

, then the broad pattern should be used for forwarding:

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]));
}

      

+4


source


The best workaround is to provide non-templated overloads for std::string

and std::wstring

that pass a template to a function ( Demo at Coliru ):

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); }

      

0


source







All Articles