1

Most people when declaring strings in C++, or most other languages, do it like so:

std::string example = "example";

However I've seen some code samples where it is done like this:

std::string example("example");

To me it seems like it needlessly obfuscates the code, particularly if there is a using std::string statement hiding somewhere above the declaration in the code making it look like

string example("example");

To some who may be new to the codebase or are coming from other languages it almost looks like it could be a method or a function.

Is there any practical or performance based reason for using the constructor instead of the assignment operator or does it come down to just personal preference?

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • 5
    There is no "practical or performance based reason". And, just to welcome you to C++, these days we will also write `string example{"example"};`. – Sam Varshavchik Aug 11 '22 at 11:21
  • 2
    One reason can be that it **isn't** the assignment operator that is used, It just looks like that (an inheritance from C). Therefore some people prefer to use `=` only for *real* assigment, and not for initialization. – BoP Aug 11 '22 at 11:22
  • 6
    `auto example{"example"s};` – Eljay Aug 11 '22 at 11:25
  • The reason is uniformity. Explicit constructors require direct initialization syntax. Not to mention it's the only way to initialize members in a constructor. Can't blame folks who opt to be uniform. – StoryTeller - Unslander Monica Aug 11 '22 at 11:38
  • 4
    Because it reflects what happens. In both cases, your examples are actually calling a constructor which accepts a `const char *` to initialise `example` with the literal `"example"`. In other words, your two examples - despite the syntactic differences - do EXACTLY the same thing. The main difference is the way they are interpreted at times - I've lost count of the number of times I've seen people insist the first is an assignment that calls `std::string`s `operator=()` function - which it doesn't. People who get tired of fielding such claims therefore sometimes prefer the second form. – Peter Aug 11 '22 at 11:41
  • @Peter IMHO that should be an answer rather than a comment. – Jesper Juhl Aug 11 '22 at 11:43
  • 2
    Obligatory [The Nightmare of Initialization in C++](https://www.youtube.com/watch?v=7DTlWPgX6zs) by Nicolai Josuttis. An hour long presentation on the initialization syntax pain point in C++. – Eljay Aug 11 '22 at 11:44
  • Looks like someplace, somewhere, Bjarne Stoustrup is drinking Uncle Bob's tears. – ConnoisseurOfCookies Aug 11 '22 at 11:58
  • 4
    *"To some who may be new to the codebase or are coming from other languages it almost looks like it could be a method or a function."* -- Most constructors will look like a method by the logic. You just have to be used to the syntax. – Ranoiaetep Aug 11 '22 at 12:06
  • 1
    @JesperJuhl Yeah, okay. I'll tidy up and expand a little, then post as an answer. – Peter Aug 11 '22 at 13:49

2 Answers2

5

The practical reason for the second form is that it reflects what actually happens. Your two examples actually - despite their syntactic differences - do exactly the same thing.

They are both calling a constructor which accepts a const char * to initialise example with the literal "example".

The main difference is the way they are interpreted at times. I've lost count of the number of times I've seen people insist that

std::string example = "example";

does an assignment that calls std::strings operator=() function - except that it doesn't.

People who get tired of fielding such claims (and prefer that someone looking at the code can understand what it actually does, wherever possible) therefore sometimes prefer the second form;

std::string example("example");
Peter
  • 35,646
  • 4
  • 32
  • 74
1

Sometimes you don't have a choice. Many constructors are explicit (even some of std::string's constructors) and you can't call it with the type object = value syntax, and for consistency type object(value) would be much better. Being explicit prevents mistakes on things like std::vector<char> v = 'c'; or MyString s = 'v'; that doesn't work as one expects

But C++11 introduced the new {} initializer syntax and most modern coding conventions strongly prefer it, so you should use this instead

std::string example{ "example" }; // good
auto example{ "example"s };       // better

Even ISO CPP's Core C++ Guidelines suggests that in ES.23: Prefer the {} initializer syntax

This has the advantage of preventing narrowing, and it works even for in-class initialization

struct foo {
    // OK
    std::string example1 = { "example" };
    std::string example2{ "example" };
    std::string example3 = "example";

    // Doesn't work
    std::string example4("example");
};

To some who may be new to the codebase or are coming from other languages it almost looks like it could be a method or a function.

That's the well-known most vexing parse issue in C++. Both std::string example(std::string(someIdentifier)) and string example(string(someIdentifier)) can represent either a function declaration or a variable definition with initialization. If you learn C++ you have to learn the syntax

In fact most vexing parse is one of the reasons of the introduction to the new {} initialization form

See also

phuclv
  • 37,963
  • 15
  • 156
  • 475