5

I've got a couple questions that I think will be quite easy for someone with C++ experience to answer, I'll bold the quesitons for the TL;DR

Given the following code:

void stringTest(const std::string &s)
{
    std::cout << s << std::endl;
}

int main()
{
    stringTest("HelloWorld");
}

Hopefuly someone can point out the error in my thought process here:

Why does the parameter in stringTest have to be marked const when passed a C-Style string? Isn't there an implicit conversion to an std::string that takes place using its cstyle string constructor, therefore "s" is no longer a reference to a literal (and is not required to be const).

Furthermore, what would a cstyle string constructor look like, and how does the compiler know to invoke this upon seeing:

stringTest("HelloWorld");

Does it simply recognize a string literal to be something like a char*?

I've stumbled upon these questions while studying copy constructors. Another quick quesiton for my own clarification...

In the case of something like:

std::string s = "HelloWorld";

Is the cstyle string constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?:

std::string(const std::string&);
3
  • "Why does the parameter in stringTest have to be marked const" — regardless of when it's necessary, you want to make all references const that aren't actually modified. Commented Apr 11, 2012 at 16:01
  • @leftaroundabout: Fair point, I'll certainly take this to heart. Commented Apr 11, 2012 at 16:04
  • possible duplicate of How come a non-const reference cannot bind to a temporary object? Commented Apr 11, 2012 at 16:09

4 Answers 4

2

Why does the parameter in stringTest have to be marked const when passed a C-Style string?

It only has to when the parameter is a reference, since a temporary std::string is constructed from the char const* you pass in and a non-const reference to a temporary is illegal.

Does it simply recognize a string literal to be something like a char*?

A string literal is a char const array, which decays to char const*. From that, the compiler infers that it should use the non-explicit constructor std::string::string(char const *) to construct the temporary.

Is the cstyle constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?

It's a bit more complicated than that. Yes, a temporary is created. But the copy constructor may or may not be called; the compiler is allowed to skip the copy construction as an optimization. The copy constructor must still be provided, though, so the following won't compile:

class String {
    String(char const *) {}
  private:
    String(String const &);
};

int main()
{
    String s = "";
}

Also, in C++11 the move constructor will be used, if provided; in that case, the copy constructor is not required.

Sign up to request clarification or add additional context in comments.

9 Comments

+1 but you might (should) clarify that in the third case, even though the copy constructor is required, it’s not actually invoked because it will (always – regardless of optimisation?) be elided. This is rather important even though it’s “just” a permitted optimisation.
A string literal does not have char const*, it is char const[N], which is of course implicitly convertible to char const*
@KonradRudolph: did that and expanded a bit on the copy/move constructor thing.
@Riken: the copy constructor is required as a signal to the compiler that objects of a type may be copied; making it private makes them non-copyable. It's elided as an optimization, but yes, that's compiler-dependent. So, never do anything fancy in copy constructors.
@Riken In theory it’s compiler dependent; in practice, all modern compilers implement it reliably everywhere since it’s a very important and rather straightforward optimisation and the standard explicitly allows it (note: no other call may be easily omitted unless the compiler can “prove” that the effect will be the same). Only for the copy constructor is this allowed. But the copy constructor is still allowed because a class may intentionally (!) forbid this behaviour by making the copy constructor inaccessible (= private).
|
2

Does it simply recognize a string literal to be something like a char*?

This part of the original question wasn't answered as clearly as I'd have liked. I fully endorse (and up-voted) Yossarian's answer for the rest though.

Basically, you need to understand what the compiler is doing when it sees a string literal in the code. That array of chars (as any c-style string really is) is actually stored in a completely different location than the code it's a part of (depending on the architecture, numeric literals can be stored at the location itself as part of the assembly/binary instruction). The two blocks of code here are "more or less" equivalent (ignore lack of includes or namespace declarations) :

int main(void)
{
    cout << "Hello!" << endl;
    return 0;
}

This is closer to what's "really" happening:

const char HELLO_STR[] = { 'H', 'e', 'l', 'l', 'o', '!', 0 };

int main(void)
{
    cout << HELLO_STR << endl;
    return 0;
}

Forgive me if I made an error in array init or whatever, but I think this expresses what I mean as for where the string literal is "really" stored. It's not in-line, but is an invisible constant to another part of the program where it's defined. In addition, some (most?) compilers out there also arrange the string literals "together" so that if you have the same literal used in 50 places, it only stores one of them, and all of them refer back to the same constant, saving memory.

So remember that any time you're using a string literal, you're using a const char[N] that exists "invisibly" somewhere, that is implicitly converted to const char*.

Comments

2

Why does the parameter in stringTest have to be marked const when passed a C-Style string?

EDIT: Temporaries must be immutable. See larsmans comment and answer, he is right.

Simple reason:

void change(std::string& c) { c = "abc"; }
change("test"); // what should the code exactly do??

Furthermore, what would a cstyle string constructor look like, and how does the compiler know to invoke this upon seeing:

It looks up std::string for string(char*) constructor

In the case of something like:

std::string s = "HelloWorld";

Is the cstyle constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?: std::string(const std::string&);

No. In this exact case (TYPE variable = SOMETHING), it is the same as writing TYPE variable(SOMETHING);. So, no copying is used.

2 Comments

The const& is there because mutable references to temporaries are illegal, not because of the constructor being non-explicit.
@KonradRudolph, I tried it without any optimization enabled in MSVC, and the TYPE variable=SOMETHING; ran constructor. I think the behavior is described somewhere, just cannot find it onw
0

The const string & s is in this example required to invoke the constructor from the argument "HelloWorld". The constructor used is the type-conversion construction.

A string& s won't do because s is directly referencing a string object.

The type conversion is defined by something similar to

 basic_string(const _CharT* __s);

With a typedef

typedef basic_string<char>    string; 

So the declaration would evaluate to

basic_string(const char * __s)    

2 Comments

That sounds wrong. Either you are mistaken or the explanation is unclear. Either way, const is not required to invoke a copy constructor.
@KonradRudolph It sounded wrong because it was wrong, my thinking cap was on backwards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.