Rule M8-5-2 (required, implementation, automated)

Braces shall be used to indicate and match the structure in the nonzero initialization of arrays and structures. See MISRA C++ 2008 [7]

Rule A8-5-2 (required, implementation, automated) Bracedinitialization {}, without equals sign, shall be used for variable initialization.

Rationale

Braced-initialization using {} braces is simpler and less ambiguous than other forms of initialization. It is also safer, because it does not allow narrowing conversions for numeric values, and it is immune to C++’s most vexing parse. The use of an equals sign for initialization misleads into thinking that an assignment is taking place, even though it is not. For built-in types like int, the difference is academic, but for user-defined types, it is important to explicitly distinguish initialization from assignment, because different function calls are involved. Note that most vexing parse is a form of syntactic ambiguity resolution in C++, e.g. “Class c()” could be interpreted either as a variable definition of class “Class” or a function declaration which returns an object of type “Class”. Note that in order to avoid grammar ambiguities, it is highly recommended to use only braced-initialization {} within templates.

Exception

If a class declares both a constructor taking std::initializer_list argument and a constructor which invocation will be ignored in favor of std::initializer_list constructor, this rule is not violated by calling a constructor using () parentheses, see A8-5-4.

Example

// $Id: A8-5-2.cpp 289436 2017-10-04 10:45:23Z michal.szczepankiewicz $
#include <cstdint>
#include <initializer_list>
void F1() noexcept
{
std::int32_t x1 =
// std::int32_t y {7.9}; // Compliant - compilation error, narrowing
std::int8_t x2{50};
// Compliant
std::int8_t x3 = {50}; // Non-compliant - std::int8_t x3 {50} is equivalent
// and more readable
std::int8_t x4 =
std::int8_t x5 = 300; // Non-compliant - narrowing occurs implicitly
std::int8_t x6(x5);
// Non-compliant
}
class A
{
public:
A(std::int32_t first, std::int32_t second) : x{first}, y{second} {}

private:
std::int32_t x;
std::int32_t y;

};
struct B
{
std::int16_t x;
std::int16_t y;
};
class C
{
public:
C(std::int32_t first, std::int32_t second) : x{first}, y{second} {}
C(std::initializer_list<std::int32_t> list) : x{0}, y{0} {}

private:
std::int32_t x;
std::int32_t y;

};
void F2() noexcept
{
A a1{1, 5};
// Compliant - calls constructor of class A
A a2 = {1, 5}; // Non-compliant - calls a default constructor of class A
// and not copy constructor or assignment operator.
A a3(1, 5);
// Non-compliant
B b1{5, 0};
// Compliant struct members initialization
C c1{2, 2};
// Compliant C(std::initializer_list<std::int32_t>)
// constructor
is
// called
C c2(2, 2);// Compliant by exception - this is the only way to call
// C(std::int32_t, std::int32_t) constructor
C c3{{}}; // Compliant - C(std::initializer_list<std::int32_t>) constructor
// is
// called with an empty initializer_list
C c4({2, 2}); // Compliant by exception // C(std::initializer_list<std::int32_t>)
// constructor is called
};
template <typename T, typename U>
void F1(T t, U u) noexcept(false)
{
std::int32_t x = 0;
T v1(x); // Non-compliant
T v2{x}; // Compliant - v2 is a variable
// auto y = T(u); // Non-compliant - is it construction or cast?
// Compilation error
};
void F3() noexcept
{
F1(0, "abcd"); // Compile-time error, cast from const char* to int

}

See also

C++ Core Guidelines [11]: ES.23 Prefer the {} initializer syntax. C++ Core Guidelines [11]: T.68: Use {} rather than () within templates to avoid ambiguities. C++ Core Guidelines [11]: ES.64: Use the T{e} notation for construction. Effective Modern C++ [13]: Item 7. Distinguish between () and {} when creating objects.