4

Why does putting std::basic_string as a parameter of a function template will fail to deduce from const char*, but it can be deduced successfully when it is constructed directly?

#include <string>
#include <iostream>
template<class Char/*, class Traits, class Allocator*/>   
                      //^doesn't matter whether the second and third template parameter is specified
void printString(std::basic_string<Char/*, Traits, Allocator*/> s)
{
    std::cout << s;
}
int main()
{
    printString("hello");    //nope
    std::basic_string s{ "hello" };//works
}

I found a relevant post here, but the answers didn't explain the reason behind

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
sz ppeter
  • 1,698
  • 1
  • 9
  • 21
  • The compiler wouldn't be able to pick a type if multiple specializations have matching constructors. – chris Aug 10 '20 at 02:37

1 Answers1

5

Because implicit conversion (from const char* to std::basic_string<char>) is not considered in template argument deduction, which fails in deducing the template parameter Char.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

You can specify the template argument explicitly,

printString<char>("hello");

Or pass an std::basic_string explicitly.

printString(std::basic_string("hello"));
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • What counts as an "implicit conversion"? If it is a constructor which can be called with only one argument, I still get error for example putting ``std::pair`` as a parameter, and called with ``printPair({1,2})``. But it takes 2 arguments to construct. – sz ppeter Aug 10 '20 at 02:59
  • @szppeter It refers to the conversion from `const char*` to `std::basic_string`. Sorry I can't get what you meant about `std::pair`. Could you provide code snippet for it? – songyuanyao Aug 10 '20 at 03:32
  • Eg. ``template void printPair(std::pairp) { std::cout< – sz ppeter Aug 10 '20 at 03:34
  • @szppeter Same issue here. The braced-init-list `{1,2}` could convert to (construct) `std::pair`, but it's not considered in template deduction. If you specify the template arguments like `printPair({1,2});` to bypass the deduction, then `{1,2}` is converted to `std::pair` and then passed to `printPair`, everything is fine. – songyuanyao Aug 10 '20 at 03:39
  • So is it basically that any class templates cannot be deduced and constructed without specifying its type if it is a argument in a function template (in the form of my question)? – sz ppeter Aug 10 '20 at 03:47
  • 1
    @szppeter, They can be deduced, just not when you pass in a different type. Your pair still has the same fundamental problem: What would the compiler deduce `T1` and `T2` to be if multiple specializations had matching constructors? (e.g., It's perfectly possible for there to be later specializations like `pair` and `pair` that both have a constructor taking `std::initializer_list`.) It's impossible in general, and C++ isn't known for specifying how something works some of the time and then erroring when the code falls outside of those specific cases. – chris Aug 10 '20 at 03:54
  • @szppeter If it requires implicit conversion to get the type which matches function parameter exactly, yes. – songyuanyao Aug 10 '20 at 03:55