What does "return {}" statement mean in C++11?

33,516

Solution 1

return {}; indicates "return an object of the function's return type initialized with an empty list-initializer". The exact behaviour depends on the returned object's type.

From cppreference.com (because the OP is tagged C++11, I excluded the rules in C++14 and C++17; refer to the link for further details):

  • If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.
  • Otherwise, if T is an aggregate type, aggregate initialization is performed.
  • Otherwise, if T is a specialization of std::initializer_list, the T object is direct-initialized or copy-initialized, depending on context, from the braced-init-list.
  • Otherwise, the constructors of T are considered, in two phases:

    • All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list
    • If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all).
  • Otherwise (if T is not a class type), if the braced-init-list has only one element and either T isn't a reference type or is a reference type that is compatible with the type of the element, T is direct-initialized (in direct-list-initialization) or copy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.

  • Otherwise, if T is a reference type that isn't compatible with the type of the element. (this fails if the reference is a non-const lvalue reference)
  • Otherwise, if the braced-init-list has no elements, T is value-initialized.

Before C++11, for a function returning a std::string, you would have written:

std::string get_string() {
    return std::string();
}

Using the brace syntax in C++11, you don't need to repeat the type:

std::string get_string() {
    return {}; // an empty string is returned
}

return NULL and return nullptr should be used when the function returns a pointer type:

any_type* get_pointer() {
    return nullptr;
}

However, NULL is deprecated since C++11 because it is just an alias to an integer value (0), while nullptr is a real pointer type:

int get_int() {
    return NULL; // will compile, NULL is an integer
}

int get_int() {
    return nullptr; // error: nullptr is not an integer
}

Solution 2

This is probably confusing:

int foo()
{
  return {};   // honestly, just return 0 - it's clearer
}

This is probably not:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}

Solution 3

return {}; means that {} is the initializer for the return value. The return value is list-initialized with an empty list.


Here is some background on the return value, based on [stmt.return] in the C++ Standard:

For a function that returns by value (i.e. the return type is not a reference and not void), there is a temporary object called the return value. This object is created by the return statement, and its initializers depend on what was in the return statement.

The return value survives until the end of the full-expression in the code that called the function; if it has class type, then its destructor will run unless it has lifetime extended by the caller binding a reference directly to it.

The return value can be initialized in two different ways:

  • return some_expression; - the return value is copy-initialized from some_expression
  • return { possibly_empty_list }; - the return value is list-initialized from the list.

Assuming T is the function's return type, then note that return T{}; is different to return {}: in the former, a temporary T{} is created, and then the return value is copy-initialized from that temporary.

This will fail to compile if T has no accessible copy/move-constructor, but return {}; will succeed even if those constructors are not present. Accordingly, return T{}; may show side-effects of the copy-constructor etc., although this is a copy elision context so it may not.


Here's a brief recap of list-initialization in C++14 (N4140 [dcl.init.list]/3), where the initializer is an empty list:

  • If T is an aggregate, then each member is initialized from its brace-or-equal-initializer if it had one, otherwise as if by {} (so apply these steps recursively).
  • If T is a class type with a user-provided default constructor, that constructor is called.
  • If T is a class type with an implicitly-defined, or = defaulted default constructor, the object is zero-initialized and then the default constructor is called.
  • If T is a std::initializer_list, the return value is an empty such list.
  • Otherwise (i.e. T is a non-class type -- return types cannot be arrays), the return value is zero-initialized.

Solution 4

It's a sort of short hand for a new instance of the methods return type.

Share:
33,516

Related videos on Youtube

Pedia
Author by

Pedia

By day: class Player By night: class Player Otherwise: class Player

Updated on September 25, 2020

Comments

  • Pedia
    Pedia almost 4 years

    What does the statement

    return {};
    

    in C++11 indicate, and when to use it instead of (say)

    return NULL;
    

    or

    return nullptr;
    
    • Richard Hodges
      Richard Hodges almost 8 years
      it returns a default-constructed instance of the function's return type.
    • i486
      i486 almost 8 years
      Or it is simple return; without value?
    • Pedia
      Pedia almost 8 years
      No, as the discussion reveals, it is a compile-time error if your function should return something (i.e. not of void return type) and you write just return; On the other hand return{}; is valid if you have a return type.
    • M.M
      M.M almost 8 years
      @Pedia Not always, some object will require arguments to construct
  • Pedia
    Pedia almost 8 years
    So, it is a compile-time error if the return type does not have a default constructor, correct ?
  • Richard Hodges
    Richard Hodges almost 8 years
    @KarimPedia it certainly should be.
  • Oktalist
    Oktalist almost 8 years
    It's a compile error if the return type is a class that does not have a non-explicit default constructor and is not an aggregate.
  • celtschk
    celtschk almost 8 years
    If the type has an initializer_list constructor, wouldn't that be used if no default constructor is available?
  • Captain Giraffe
    Captain Giraffe almost 8 years
    @Oktalist I don't think the non-explicit is in play here.
  • Damon
    Damon almost 8 years
    @celtschk: Luckily, no. Don't give them ideas! An unnamed pair of parentheses or braces creates a nameless temporary value-initialized to whatever the "zero" value is according to the default constructor. An initializer list is created in combination with auto and at least one argument. Except... if you are using a compiler that implements N3922, in that case the fun begins, because then it matters for single-value braces whether there's a = or not... a very, very ingenious idea to make it different in every case.
  • T.C.
    T.C. almost 8 years
    @celtschk Yes, it will. List-initialization is a fairly tricky area.
  • MickeyfAgain_BeforeExitOfSO
    MickeyfAgain_BeforeExitOfSO almost 8 years
    "probably confusing" ? Is this why some unnamed soul referred to "That bloated obscenity that is C++"? Can whatever saving in keystrokes this provides possibly justify the potential for lack of clarity it offers? This is a sincere question. Please convince me with practical examples.
  • Oktalist
    Oktalist almost 8 years
    @mickeyf Using {} instead of 0 doesn't save any keystrokes.
  • M.M
    M.M almost 8 years
    I would prefer return {}; in the int case
  • M.M
    M.M almost 8 years
    return {} is NOT equivalent to return SomeObjectWithADefaultConstructor{};
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica almost 8 years
    @mickeyf In all honesty, I think return {} is pretty clear, once you get used to it. It's pretty easy to read it as "See the return type? It's default-constructible, so return one of those." ...Of course, if it's not default-constructible, that's when the fun begins.
  • Richard Hodges
    Richard Hodges almost 8 years
    @mickeyf in all honesty, I agree with you. syntax shortcuts can be fun and newbies find them impressive, but they are horrid to debug and maintain. I have learned to appreciate pedantic old programmers who spell everything out explicitly.
  • T.C.
    T.C. almost 8 years
    Aggregate init comes first, and it recursively initializes every member with {}, which may or may not be value-init.
  • M.M
    M.M almost 8 years
    @T.C. right, I went by cppreference but overlooked an "until C++14"