Search code examples
c++c++14c++17

string constructor taking two char* into another std::string works in c++14 but not c++17


The following program attempts to construct a second string using the first string and a pointer into the middle of the first string:

#include <string>

int main() {
  std::string src = "hello world";
  const char* end = &src[5];
  std::string dest(src.data(), end);
}

In C++14 and earlier this works. But in C++17 the call fails:

error: no matching function for call to ‘std::__cxx11::basic_string<char>::basic_string(char*, const char*&)’
   std::string dest(src.data(), end);
[... full output omitted ...]

What changed to make this fail?


Solution

  • The construction of dest is attempting to use the following constructor (from cppreference):

    template< class InputIt >
    basic_string( InputIt first, InputIt last, 
                  const Allocator& alloc = Allocator() );
    

    This requires that first and last have the exact same type. The problem is that in C++17 std::string::data returns a non-const pointer when called on a non-const std::string. This means the type of first is char* and the type of last is const char*. Since they differ, the template argument InputIt cannot be deduced and the call fails.

    There is no way to explicitly specify the template argument to a constructor call, but there is a solution. std::string::c_str still returns a const char* and the construction of dest can use it:

    std::string dest(src.c_str(), end);
    

    Another solution would be calling data() on a str through a const reference:

    const auto& const_src = src;
    std::string dest(const_src.data(), end);
    

    Or if you don't care about C++14 compatability you can use std::as_const (thanks Mário Feroldi)

    std::string dest(std::as_const(src).data(), end);