Inconsistency constructing std::string from C-string vs. from another std::string

Viewed 55

Why does the std::string constructor produce different output depending only on what kind of string it is constructed from?

For example, consider the following C++ code:

#include <iostream>
#include <string>

int main() {
    char cstyle[] = "01234567";
    std::string std = "01234567";
    std::cout << std::string(cstyle, 4) << std::endl;
    std::cout << std::string(std, 4) << std::endl;
    return 0;
}

which produces the following output:

0123
4567

I would expect them both to produce 0123, or both to produce 4567, but I wouldn't expect one to produce 0123 and the other to produce 4567.

Why this inconsistency?

EDIT: This question isn't about which default constructor is called, or whether they do different things. They do different things, and different constructors are called. This question is about why they don't do the same thing. In other words, this is more of a philosophical question than a technical question.

1 Answers

In each case, there is an overload with and without further integer parameters; we may consider them to be optional. For the constructors from a const char*, the one additional parameter is the size (which is needed if the input is not null-terminated); if you want a starting offset, you can just write std::string(p+off,sz), so there’s no point in offering it as a parameter.

When copying another string object, however, std::string(s.data()+off,sz) is circuitous and unsafe. Without the source object, it’s impossible to prevent overrunning it with the offset or an explicit size; historically, data() has not always been (guaranteed to be) null-terminated, making such usage unsafe without the size. As such, both a starting offset and size are provided for the copy-like constructors—in that order because that’s much more natural—and if you provide exactly one it must be the offset.

Related