Why isn't string assignment optimised when the

2019-08-09 18:43发布

问题:

I was playing around today with some timing code and discovered that when asigning a string literal to std::string, that it was around 10% faster (with a short 12 char string, so likly even bigger difference for large strings) to do so with a literal of known length (using the sizeof operator) than not. (Only tested with the VC9 compiler, so I guess other compilers may do it better).

std::string a("Hello World!");
std::string b("Hello World!", sizeof("Hello World!");//10% faster in my tests

Now the reason I suspect is for a it has to call strlen (VC9 goes into assembly which isnt a strong point of mine so I cant be 100% sure) to get the string length, then do the same as the second case does anyway.

Given how long std::string has been around, and how common the first case is (especially if you include +, =, +=, etc operators and equivalent methods) in real world programs how come it doesn't optimise the first case into the second? It seems a really simple one as well to just say if it's an std::basic_string object and a literal, compile it as if it was written like b?

回答1:

The first can't be optimised into the second. In the first, the length of the string is unknown and so has to be calculated, in the second you tell it how long it is, so no calculation is needed.

And using sizeof() makes no difference - that is calculated at compile time too. The constructor that the first case uses is:

 string( const char * s );

there is no way of this constructor detecting it is being given a string literal, much less calculating its length at compile time.

Also, constructing strings from C-style string literals happens relatively rarely in real code - it simply isn't worth optimising. And if you do need to optimise it, simply re-write:

while( BIGLOOP ) {
   string s( "foobar" );
   ...
}

as:

string f( "foobar" );
while( BIGLOOP ) {
   string s( f );
   ...
}


回答2:

The compiler undoubtedly could do something like this, and actually you could do this yourself:

template<size_t SIZE>
std::string f(const char(&c)[SIZE]) {
    return std::string(c, SIZE);
}

int main() {
    std::string s = f("Hello");
    cout << s;
}

or even with a custom derived type (though there is no reason std::string couldn't have this constructor):

class mystring : public string {
public:
    template<size_t SIZE>
    mystring(const char(&c)[SIZE]) : string(c, SIZE) {}
};

int main() {
    mystring s("Hello");
    cout << s;
}

One large drawback is that a version of the function/constructor is generated for every different string size, and the whole class could even be duplicated if the compiler doesn't handle template hoisting very well... These could be deal-breakers in some situations.