Difference between `vector<int> v;` and `vector<int> v = vector<int>();`

Starting from C++17 there's no difference whatsoever.

There's one niche use case where the std::vector = std::vector initialization syntax is quite useful (albeit not for default construction): when one wants to supply a "count, value" initializer for std::vector<int> member of a class directly in the class's definition:

struct S {
  std::vector<int> v; // Want to supply `(5, 42)` initializer here. How?
};

In-class initializers support only = or {} syntax, meaning that we cannot just say

struct S {
  std::vector<int> v(5, 42); // Error
};

If we use

struct S {
  std::vector<int> v{ 5, 42 }; // or = { 5, 42 }
};

the compiler will interpret it as a list of values instead of "count, value" pair, which is not what we want.

So, one proper way to do it is

struct S {
  std::vector<int> v = std::vector(5, 42);
};

The 1st one is default initialization, the 2nd one is copy initialization; The effect is same here, i.e. initialize the object v via the default constructor of std::vector.

For std::vector<int> v = std::vector<int>();, in concept it will construct a temporary std::vector then use it to move-construct the object v (note there's no assignment here). According to the copy elision (since C++17 it's guaranteed), it'll just call the default constructor to initialize v directly.

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:

  • In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

    T x = T(T(f())); // only one call to default constructor of T, to initialize x
    

(Before C++17, the copy elision is an optimization.)

this is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all),

BTW: For both cases, no std::vector objects (including potential temporary) will be constructed with dynamic storage duration via new expression.