Direct initialization

From cppreference.com
< cpp‎ | language
 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++20)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
decltype (C++11)
auto (C++11)
alignas (C++11)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Implicit conversions - Explicit conversions
static_cast - dynamic_cast
const_cast - reinterpret_cast
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static
Special member functions
Templates
Miscellaneous
 
 

Initializes an object from explicit set of constructor arguments.

Syntax

T object ( arg );

T object ( arg1, arg2, ... );

(1)
T object { arg }; (2) (since C++11)
T ( other )

T ( arg1, arg2, ... )

(3)
static_cast< T >( other ) (4)
new T(args, ...) (5)
Class::Class() : member(args, ...) { ... } (6)
[arg](){ ... } (7) (since C++11)

Explanation

Direct initialization is performed in the following situations:

1) initialization with a nonempty parenthesized list of expressions or braced-init-lists (since C++11)
2) initialization of an object of non-class type with a single brace-enclosed initializer (note: for class types and other uses of braced-init-list, see list-initialization) (since C++11)
3) initialization of a prvalue temporary (until C++17)the result object of a prvalue (since C++17) by function-style cast or with a parenthesized expression list
4) initialization of a prvalue temporary (until C++17)the result object of a prvalue (since C++17) by a static_cast expression
5) initialization of an object with dynamic storage duration by a new-expression with a non-empty initializer
6) initialization of a base or a non-static member by constructor initializer list
7) initialization of closure object members from the variables caught by copy in a lambda-expression

The effects of direct initialization are:

  • If T is an array type,
  • the program is ill-formed.
(until C++20)
struct A
{
    explicit A(int i = 0) {}
};
 
A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A()
A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1]
              //        from {} selected explicit constructor
(since C++20)
  • If T is a class type,
  • if the initializer is a prvalue expression whose type is the same class as T (ignoring cv-qualification), the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object.
    (Before C++17, the compiler may elide the construction from the prvalue temporary in this case, but the appropriate constructor must still be accessible: see copy elision)
(since C++17)
  • the constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.
  • otherwise, if the destination type is a (possibly cv-qualified) aggregate class, it is initialized as described in aggregate initialization except that narrowing conversions are permitted, designated initializers are not allowed, a temporary bound to a reference does not have its lifetime extended, there is no brace elision, and any elements without an initializer are value-initialized.
struct B
{
    int a;
    int&& r;
};
 
int f();
int n = 10;
 
B b1{1, f()};            // OK, lifetime is extended
B b2(1, f());            // well-formed, but dangling reference
B b3{1.0, 1};            // error: narrowing conversion
B b4(1.0, 1);            // well-formed, but dangling reference
B b5(1.0, std::move(n)); // OK
(since C++20)
  • Otherwise, if T is a non-class type but the source type is a class type, the conversion functions of the source type and its base classes, if any, are examined and the best match is selected by overload resolution. The selected user-defined conversion is then used to convert the initializer expression into the object being initialized.
  • Otherwise, if T is bool and the source type is std::nullptr_t, the value of the initialized object is false.
  • Otherwise, standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T, and the initial value of the object being initialized is the (possibly converted) value.

Notes

Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.

In case of ambiguity between a variable declaration using the direct-initialization syntax (1) (with round parentheses) and a function declaration, the compiler always chooses function declaration. This disambiguation rule is sometimes counter-intuitive and has been called the most vexing parse.

#include <iterator>
#include <string>
#include <fstream>
int main()
{
    std::ifstream file("data.txt");
    // the following is a function declaration:
    std::string str(std::istreambuf_iterator<char>(file),
                    std::istreambuf_iterator<char>());
    // it declares a function called str, whose return type is std::string,
    // first parameter has type std::istreambuf_iterator<char> and the name "file"
    // second parameter has no name and has type std::istreambuf_iterator<char>(),
    // which is rewritten to function pointer type std::istreambuf_iterator<char>(*)()
 
    // pre-C++11 fix: extra parentheses around one of the arguments
    std::string str( (std::istreambuf_iterator<char>(file) ),
                      std::istreambuf_iterator<char>());
 
    // post-C++11 fix: list-initialization for any of the arguments
    std::string str(std::istreambuf_iterator<char>{file}, {});
}

Example

#include <string>
#include <iostream>
#include <memory>
 
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
 
int main()
{
    std::string s1("test"); // constructor from const char*
    std::string s2(10, 'a');
 
    std::unique_ptr<int> p(new int(1));  // OK: explicit constructors allowed
//  std::unique_ptr<int> p = new int(1); // error: constructor is explicit
 
    Foo f(2); // f is direct-initialized:
              // constructor parameter n is copy-initialized from the rvalue 2
              // f.mem is direct-initialized from the parameter n
//  Foo f2 = 2; // error: constructor is explicit
 
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

Output:

test aaaaaaaaaa 1 2

See also

This site archives manuals. You are looking at an archived manual.
If you're looking for this specific version of this manual, you're in the right place. Otherwise, please check the version.