4

Here is a question from an interiew, and the code attached below. What is wrong with this function?

string f() {
    return "hello world";
}

To me, there is nothing wrong at all, and I can even run a program using this function:

#include <iostream>
#include <string>
using namespace std;

string f() {
    return "hello world";
}

int main() {
    string s2=f();
    cout<<s2<<endl;
}

What is wrong with this function?

  • Horrible naming, no reason to be a function (could just be a const variable)... that's about it. – David Nov 01 '13 at 02:12
  • 14
    Ah, the old "Can you guess what I'm thinking" interview question. – Retired Ninja Nov 01 '13 at 02:12
  • It's horrible would be my first answer. Namespace std in global scope. Badly named functions. No clear comments about what it is supposed to be doing. No chance for the user to see the output (from windows anyway). – David Nov 01 '13 at 04:45

3 Answers3

6

Well... have some consolation, that in this type of question, there's often no right answer. The best you might hope is to wow the interviewers by showing your expertise to elaborate the pros and cons, (a.k.a. code masturbation).

From a style point of view, its often bad to return objects using return-by-value. Consider:

X f();

X x;
x = f();

f allocates an X. An X needs to be returned, so an additional copy is placed on the stack as the return value. Finally, the x on the stack is copied into x via the assignment operator. In total, 3 X's appear in memory. Some might say this is inefficient, and so you should try to not return-by-value, when the value is an object.

However, a more savvy interviewee might have pointed out that:

  • some compilers optimize out the temporary copy .For this optimization, I believe it makes a difference here whether the assignment to X is made via the copy constructor or equality operator. Say "copy elison" and see if the interviewer raise and eyebrow: What is copy elision and how does it optimize the copy-and-swap idiom? . I'm guessing copy elision might work here because the function is in the same translation unit, and therefore easily inlineable.
  • almost all implementations of the string class implement copy on write, in which a single copy of the string's text data would be held in this case, with the rest of the data being held on the stack and having basically causing no overhead when the string is copied.
  • if you're working with an early version of visual studio, memory leaks will ensue

So, what alternatives?

X const& f();

may be considered as an alternative. However, this has its own style problems as the lifetime of the result is unclear to the caller. Perhaps the next call invalidates the previous reference result? Who knows?

Also

void f_get( X& result );

may be preferred (by some interviewers). This has the benefits:

  • no copies of local variables
  • no references/pointers of indeterminate lifetimes
  • in the case where X is string, one only copy of the text data is ever held (although this is true is all cases)

In both, readability is sacrificed -- in the general cases, the caller now has to pay more attention to which arguments are functional parameters, and which are intended to hold results.

In the O.P. it not may also not be obvious what the lifetime of string literal on the stack is. Is it de-allocated from the stack when the function returns? Probably not -- its likely kept in the static memory area (best check the spec to be sure). But if not, then what's the consequence - probably none since the string probably makes a copy of char const* on the heap, unless you have some weird string implementation who's constructors can tell the distinguish literal from non-literal parameters with some clever template type-trait magic.

In any case, spouting some B.S. like this would have surely scored points, if off the mark.

Also, is your console outputting ascii, UTF-8 or unicode? The program's probably wrong for one of them, and also wrong in no English speaking locales.

Did you check that stdout has a valid value, or are you compiling without -D_CONSOLE in windows, or for some embedded device or gaming console where stdout is not available and you must redirect out to a logging library (or crash)?

Meh... take your pick.

The temporary copy was probably the droid they were looking for. Because the temporary is of no consequence in the case of a string (and probably also copy elision), the interviewer smells of elderberries and his mother was a goat

Community
  • 1
  • 1
user48956
  • 14,850
  • 19
  • 93
  • 154
  • 1
    You can't make a reference `const`... it is already immutable during its lifetime. – Ben Voigt Nov 01 '13 at 03:50
  • You're right. I was thinking about X &const x and X const& x, which are the same. Have removed that from the post. – user48956 Nov 01 '13 at 04:57
  • They are not the same. `X& const x` (which won't compile when written like this but you can achieve the same effect with `typedef`s) is the same as `X& x`. – Simple Nov 01 '13 at 09:39
4

There's nothing wrong per se with this function - it's totally safe. Since this function always returns the same string, however, you could consider replacing the function with a constant, like this:

static const string kHelloWorld = "hello world";

cout << kHelloWorld << endl;

This has the advantage that there's only one copy of this string ever and it now is a named constant, meaning that it can be changed as necessary and there's no overhead for the function call and return.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Making the function inline would have the same effect, wouldn't it? I suppose it makes less sense than a constant, though. – chris Nov 01 '13 at 02:12
  • 1
    @chris- Not really. The function creates a new string object and returns it each time, so even if it were inline there still would be a new object created. Making it a constant means that no copies are ever made. – templatetypedef Nov 01 '13 at 02:13
  • But I think making it static, you are keeping this object in memory as long as program is running (Global memory space). In other case f(), it takes space in local memory. – Ashok Nov 01 '13 at 02:19
  • @Ashok- That is true. Even putting the string literal into the program will increase the amount of memory the program needs (since that literal has to be stored somewhere in the program binary), so I doubt that the overhead for a string object, especially given the small string optimization that's often used, will have that much of an impact on program memory relative to just the literal itself. – templatetypedef Nov 01 '13 at 02:21
  • @templatetypedef, Could the compiler not optimize that out if the function is inlined? I'm not great with compiler optimizations, but it seemed like a good possibility. – chris Nov 01 '13 at 02:27
  • @chris- I believe that it probably could. However, you'd still have to create a new object somewhere to hold the return value. If there's a global fixed string, then one object is created at program startup, at which point no copies are made unless you explicitly copy it. – templatetypedef Nov 01 '13 at 02:27
  • @templatetypedef, Okay, that's fair enough. – chris Nov 01 '13 at 02:29
  • @PascalCuoq- Every time the function is called, a new `string` object needs to be created to hold the return value. This means that over the lifetime of the program, there might be a lot of string objects created and destroyed. The constant definition means that only one object is created ever. – templatetypedef Nov 01 '13 at 02:29
  • *does* it always return the same std::string? – kfsone Nov 01 '13 at 03:36
  • @kfsone- Each call to the function needs to return a distinct `std::string` object, so it should have to return different strings each time. – templatetypedef Nov 01 '13 at 03:37
  • Your answer says "always returns the same string", may want to reword that. – kfsone Nov 01 '13 at 03:41
  • @kfsone: It does return the same string (a sequence of characters), but not the same `std::string` object. I think the phrase you are complaining about is quite clear *in context*. – Ben Voigt Nov 01 '13 at 04:16
2
string f() {
    return "hello world";
}

As per templatetypedef's answer, this will result in the construction of a string temporary at each point of use.

You can address this with:

const std::string& f()
{
    static const std::string hw("Hello world");
    return hw;
}

This way, multiple calls yield references to a single std::string instance, which is created during the first call. There may be a performance cost each time f() is called, while it checks if local statics have already been created - I'm not sure what the state of the art in compiler approaches to this is.

By way of contrast, if you have client code directly use a constant...

static const std::string hw("Hello world");

...then you have to get every client to update their code if you ever want to start returning a runtime-determined value (e.g. picking a "Hello world" message in a language based on an environment variable). If you'd stuck with the function, then you could choose which of several constants to return a reference to, or you could change the return type from by-const-reference to by-value and return any dynamically generated text.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252