15

Here is a code sample from a C++ quiz:

#include <iostream>
struct X {
    X(const char *) { std::cout << 1; }
    X(const X &) { std::cout << 2; }
    X(X &&) { std::cout << 3; }
};

X f(X a) {
    return a;
}

X g(const char * b) {
    X c(b);
    return c;
}

int main() {
    f("hello");
    g("hello");
}

What will be the output of the program?

I think this way:

  1. f(X a) is called, and the constructor implicitly converts const char* to X, so the output is 1
  2. As we don't have an object to store the return value in, the return value is discarded, no output
  3. g(const char*) is called, and X c(b) X(const char*) The output is 1
  4. The return value is one more time discarded - no output

So the answer is 11. The answer which is given to the quiz is 131. The answer, which I get with g++ 4.4.4-13 is 121.

It is said that this code was compiled with this command:

g++ -std=c++11 -Wall -Wextra -O -pthread

Where does the middle number come from? And why can it be 3 or 2?

6
  • Copy elision (en.cppreference.com/w/cpp/language/copy_elision) is not mandatory. You cannot rely on this optimization. So the return statements output nothing cannot be relied upon.
    – Lingxi
    Commented Aug 12, 2015 at 7:33
  • 5
    Who wrote this as a "quiz"? This program can validly print four different outputs.
    – T.C.
    Commented Aug 12, 2015 at 7:33
  • @T.C. It is something called C++ pub quiz at ACCU 2014 year Commented Aug 12, 2015 at 7:37
  • 3
    Don't forget that gcc 4.4 is a 2009 compiler, so implicit move on return was not there yet.
    – ColdCat
    Commented Aug 12, 2015 at 7:57
  • 2
    How can gcc 4.4 even accept -std=c++11? The only thing it knows is -std=c++0x.
    – Ruslan
    Commented Aug 12, 2015 at 9:28

2 Answers 2

17

In theory, this can print any of 131, 13313, 1313, and 1331. It's pretty silly as a quiz question.

  • f("hello");:

    • "hello" is converted to a temporary X via the converting constructor, prints 1.
    • The temporary X is used to initialize the function argument, calls the move constructor, prints 3. This can be elided.
    • x is used to initialize the temporary return value, calls the move constructor, prints 3. It's a function parameter, so no elision is allowed, but the return is an implicit move.
  • g("hello");

    • "hello" is used to construct c via the converting constructor, prints 1.
    • c is used to initialize the temporary return value, calls the move constructor, prints 3. This can be elided.

Remember that functions always have to construct the thing they return, even if it's just discarded by the calling code.

As to printing 2, that's because the ancient compiler you are using doesn't implement the implicit-move-when-returning-a-local-variable rule.

6
  • 2
    Is there a reason why a temporary has to be used (and possibly elided) for initialising the parameter of f? Why cannot the parameter be initialised directly from the argument? Commented Aug 12, 2015 at 7:47
  • 1
    @Angew Argument passing is copy-initialization. (Insert lengthy quote from [dcl.init]/17.6.2 here)
    – T.C.
    Commented Aug 12, 2015 at 7:51
  • 11 can also be the result, if the compiler performs the NRVO. Or am I wrong? Commented Aug 12, 2015 at 10:53
  • @BЈовић No, you can't NRVO with a function parameter.
    – T.C.
    Commented Aug 12, 2015 at 14:43
  • @T.C. Any special reason? I thought it is up to compiler to apply NRVO, but is free not to do it. Commented Aug 12, 2015 at 15:00
3

Copy elision applies to the return statement in g, and possibly elsewhere. Quoted from cppreference:

Copy elision is the only allowed form of optimization that can change the observable side-effects. Because some compilers do not perform copy elision in every situation where it is allowed (e.g., in debug mode), programs that rely on the side-effects of copy/move constructors and destructors are not portable.

So, to your example code, the output cannot be reliably predicted across different implementations.

2
  • Copy elision does not apply to the return in f.
    – T.C.
    Commented Aug 12, 2015 at 7:39
  • @T.C. Oops, it is the parameter.
    – Lingxi
    Commented Aug 12, 2015 at 7:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.