To be specific: direct-list-initialization (cppreference.com (3)).
Both std::make_shared
and uniform initialization features were introduced in C++11. So we can use aggregate initialization when allocating objects on heap: new Foo{1, "2", 3.0f}
. This is a nice way to directly initialize objects that have no constructors, such as aggregates, pods, etc.
A real-life scenarios, such as declaring casual structures within a function, to efficiently supply set of arguments to a lambda became very common, in my experience:
void foo()
{
struct LambdaArgs
{
std::string arg1;
std::string arg2;
std::string arg3;
};
auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});
auto lambda = [args] {
/// ...
};
/// Use lambda
/// ...
}
Here auto args = std::make_shared<LambdaArgs>("1", "2", "3");
whould be nice but isn't going to work, because std::make_shared
is usually implemented as:
template<typename T, typename... Args>
std::shared_ptr<T> make_shared(Args && ...args)
{
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
So we're stuck with the auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});
.
The problem that was supposed to be solved with std::make_shared
still persists for object without constructor. And the workaround is not only unaesthetic but also less efficient.
Is this another oversight or are there some reasons that defend this choice. Specifically, what pitfalls can be in the list initialization solution? std::make_unique
was introduced later, in C++14, why does it too follow same pattern?