Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing.. which one is more efficient?

I have the following question. Which one of these is better that should be followed and why?

string strMyString = "SampleString";

or

string strMyString("SampleString");

Thanks in advance.

like image 774
vj01 Avatar asked Mar 12 '09 03:03

vj01


4 Answers

Compile both, look at the assembler. The first is one fewer instruction ...

; 9    :    std::string f("Hello");

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _f$[esp+80]
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

; 10   :    std::string g = "Hello";

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _g$[esp+80]
    mov DWORD PTR __$EHRec$[esp+88], 0
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

... but that's an artifact, because it was the first one the compiler saw. Change the code by swapping the order:

; 9    :    std::string g1 = "Hello";

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _g1$[esp+136]
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

; 10   :    std::string f1("Hello");

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _f1$[esp+136]
    mov DWORD PTR __$EHRec$[esp+144], 0
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

... and lo and behold, the second is one fewer instruction.

We also see that this compiler (Microsoft VC++ 2005, Release settings) generated the same assembler for both versions. So it makes no difference in this compiler, and you can prove it.

like image 142
Thomas L Holaday Avatar answered Oct 02 '22 16:10

Thomas L Holaday


I answered it here

One thing i put into this answer here: Neither is using any assignment operator.

Short explanation for the string specific thing though. std::string has a constructor taking one argument that accepts char const*:

// simplified to a normal class declaration. std::string actually
// is a template instantiation. 
class string {
public:
    string(char const* str) {
        // copy over...
    }
};

Now you see that has a constructor taking a pointer to character(s). So that it can accept a string literal. I think the following case is obvious then:

string s("hello");

It will call the constructor directly and initialize s thereby. This is called direct initialization.

The other way of initializing a variable is called copy initialization. The Standard says for the case of copy initialization where the initializer has not the type of the object it is initializing, the initializer is converted to the proper type.

// uses copy initialization
string s = "hello";

First, let's state the types

  • s has type std::string
  • "hello" is an array, which in this case again is handled like a pointer. We will therefor consider it as char const*.

The compiler looks for two ways to do the conversion.

  • Is there a conversion constructor in std::string?
  • Does the initializer has a type that has a conversion operator function returning a std::string?

It will create a temporary std::string by one of those ways that is then used to initialize the object s by using std::string's copy constructor. And it sees std::string has a conversion constructor that accepts the initializer. So it uses it. In the end, it is effectively the same as

std::string s(std::string("hello"));

Note that the form that is used in your example that triggered all that

std::string s = "hello";

defines an implicit conversion. You can mark the constructor taking the char const* as explicit for your types if you wonder about the initialization rules for your stuff, and it will not allow to use the corresponding constructor as a conversion constructor anymore:

class string {
public:
    explicit string(char const* str) {
        // copy over...
    }
};

With that, initializing it using a copy initialization and a char const* actually is forbidden now (and in various other places)!

Now, that was if the compiler does not support elision of temporaries at various places. The compiler is allowed to assume that a copy constructor copies in this context, and can eliminate the extra copy of the temporary string, and instead construct the temporary std::string directly into the initialized object. However, the copy constructor must be accessible in particular. So, the copy initialization is invalid if you do this

class string {
public:
    explicit string(char const* str) {
        // copy over...
    }

private: // ugg can't call it. it's private!
    string(string const&);
};

Now actually, only the direct initialization case is valid.

like image 45
Johannes Schaub - litb Avatar answered Oct 02 '22 16:10

Johannes Schaub - litb


The only real difference is that the first one technically requires use of the copy constructor, but the compiler is allowed to elide it so that the efficiency will be identical in both cases.

However, the first requires that the copy constructor be accessible (i.e. not private), even if it isn't actually used.

like image 45
Drew Hall Avatar answered Oct 02 '22 14:10

Drew Hall


The other answers are all correct, but please also remember that it probably doesn't matter. It's going to be rare, very rare, incredibly rare, that string initialization efficiency will ever impact your program's speed even by a fraction of a second.

The question itself is a fun one because it helps show the operations of C++ constructors and assignments, but in practice if you're spending time trying to optimize this (and posting on SO is evidence enough you are..) you're really tilting at windmills.

It's better to avoid the distraction and spend your effort elsewhere.

like image 28
SPWorley Avatar answered Oct 02 '22 14:10

SPWorley