15

I just find out that this little piece of C++ code doesn't give me the same result with clang++ and with g++:

#include <iostream>
#include <string>

using namespace std;

const string& createString(char c) {
    static string s;
    s="";
    for(int i=0; i<10; ++i) {
    s+=c;
    }
    return s;
}

int main() {
    cout << createString('a') << ' ' << createString('z') << endl;
    return 0;
}

With clang++ it writes:

aaaaaaaaaa zzzzzzzzzz

like I want it to be, but with g++ it writes:

aaaaaaaaaa aaaaaaaaaa

Why is it so? Is the g++ implementation standard compliant? And what should I do if I want a function to return a temporary "big" type by reference like here to avoid useless copy?

Cœur
  • 37,241
  • 25
  • 195
  • 267
user2174468
  • 229
  • 1
  • 6
  • 4
    Why is your `string` `static` in the first place?! – us2012 Mar 15 '13 at 15:29
  • For other readers, Note that the string is being returned by reference. – Bill Lynch Mar 15 '13 at 15:31
  • to be return by reference, it must be, no ? – user2174468 Mar 15 '13 at 15:32
  • 4
    Return the "big" string by value. In release mode, almost any compiler will apply the Return Value Optimization and it won't be copied at all. Or if you use a C++11 compiler, it will be moved instead of copied if the RVO cannot be applied. – aschepler Mar 15 '13 at 15:32
  • Your function should return "return std::string(c, 10);" without all those other lines and especially without the static. – nvoigt Mar 15 '13 at 15:38
  • @nvoigt Your constructor arguments are not in the correct order. – us2012 Mar 15 '13 at 15:41
  • You are right... copied it from the next tab and still got it wrong... "return std::string(10,c);" – nvoigt Mar 15 '13 at 15:45
  • Is your "big" type not move-constructable? If it has a fast move-constructor, just return an xvalue by value. :) – Yakk - Adam Nevraumont Mar 15 '13 at 17:14
  • That's weird, in clang++ it prints `aaaaa aaaaa` -- http://coliru.stacked-crooked.com/view?id=84def7c0630a823fe48b1002a4c4260c-41475e255c735859f2b181fd0d057a6f – David G Jun 01 '13 at 13:09

3 Answers3

23

Yes, both implementations are compliant. The order of evaluation of function arguments is not specified.

Therefore, createString('a') and createString('z') can be evaluated in any order. Furthermore, createString('z') can be evaluated before or after the result of createString('a') is written out.

Since the function is stateful, and returns the state by reference, both outputs are permissible, as is zzzzzzzzzz zzzzzzzzzz.

Finally, it is worth noting that having static state would be a major headache in a multithreaded environment.

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 3
    It isn't the order of `createString('a')` and `createString('z')`, but rather their relative order with the `<<` operators that causes the unexpected behavior. – Yakk - Adam Nevraumont Mar 15 '13 at 15:32
  • 4
    Indeed. The compiler is allowed to generate both calls to `createString()` before any calls to `operator<<()` (as g++ is apparently doing), or to intersperse them (as clang++ is doing.) So it's the relative order of all of these function calls that matters. – Ernest Friedman-Hill Mar 15 '13 at 15:33
  • @Yakk, but that order isn't specified either. A result of `aaaaaaaaaa aaaaaaaaaa` or `zzzzzzzzzz aaaaaaaaaa` would be just as valid. – Mark Ransom Mar 15 '13 at 15:44
  • 3
    @MarkRansom I don't think `zzzzzzzzzz aaaaaaaaaa` is a legal output. `createString('z')` certainly can run before `createString('a')`, but `createString('a')` must still run before the first `operator<<`. So if 'z' runs first then the output must be `aaaaaaaaaa aaaaaaaaaa` and if 'a' runs first then the output can be either `aaaaaaaaaa zzzzzzzzzz` or `zzzzzzzzzz zzzzzzzzzz`. – bames53 Mar 15 '13 at 16:00
  • @bames53 of course you're right, I wasn't thinking it through enough. – Mark Ransom Mar 15 '13 at 16:13
7

And what should I do if I want a function to return a temporary "big" type by reference like here to avoid useless copy ?

It won't be. RVO and NRVO can trivially take care of this. In addition, move semantics. In short, there's nothing problematic about returning a std::string by value at all.

Puppy
  • 144,682
  • 38
  • 256
  • 465
2

And what should I do if I want a function to return a temporary "big" type by reference like here to avoid useless copy ?

Call it only once per expression. For example, this will work fine:

std::cout << createString('a') << ' ';
std::cout << createString('z') << std::endl;
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720