Make_Unique and Perfect Forwarding

make_unique and perfect forwarding

Herb Sutter, chair of the C++ standardization committee, writes on his blog:

That C++11 doesn’t include make_unique is partly an oversight, and it will almost certainly be added in the future.

He also gives an implementation that is identical with the one given by the OP.

Edit: std::make_unique now is part of C++14.

Perfect forwarding in std::make_uniqueSomeWrapperT not quite perfect

Why does this code compile:

auto compiles = std::make_unique<DataHolder<int>>(42);
auto alsoCompiles = std::make_unique<DataHolder<std::string>>("Hi");

DataHolder<int> has a constructor taking int, and 42 is passed as the constructor argument to construct DataHolder<int>, which works fine (similarly as DataHolder<int>(42)).

DataHolder<std::string> has a constructor taking std::string, and "Hi" is passed as the constructor argument, it could convert to std::string implicitly, which works fine (similarly as DataHolder<std::string>("Hi")).

and this code does not:

auto doesnt = std::make_unique<DataHolder<Point>>({20, 21});
auto alsoDoesnt = std::make_unique<DataHolder<Widget>>("Hello");

std::make_unique<DataHolder<Point>>({20, 21}) doesn't work because braced-init-list like {20, 21} doesn't have type, template argument deduction for std::make_unique fails on it. This belongs to non deduced contexts.


  1. The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:

DataHolder<Widget> has a constructor taking Widget, and "Hello" is a const char[6] which could decay to const char*, then two user-defined conversions are required, one from const char* to std::string, one from std::string to Widget, but only one user-defined conversion is allowed in one implicit conversion sequence. (Similarly DataHolder<Widget>("Hello") doesn't work either.)

If you pass Point and std::string directly, they would work fine.

auto doesnt = std::make_unique<DataHolder<Point>>(Point{20, 21});;
auto alsoDoesnt = std::make_unique<DataHolder<Widget>>(std::string{"Hello"});

As @YiFei suggested, you can make DataHolder's constructor template, and forward all the arguments by std::forward to the constructor of T to initialize the data member directly, i.e. work in the similar way as std::make_unique.

E.g.

template <typename... Args>
DataHolder(Args&&... value) : value {std::forward<Args>(value)...} {}

Note that std::make_unique<DataHolder<Point>>({20, 21}); still doesn't work for the reason above. But you can do std::make_unique<DataHolder<Point>>(20, 21); instead, 20 and 21 are forwared to the constructor of Point taking two ints and then works fine. Similarly for std::make_unique<DataHolder<Widget>>("Hello"), "Hello" is forwarded to the constructor of Widget taking std::string, "Hello" could convert to std::string implicitly and then works fine.

Perfect forwarding of a braced initializer to a constructor?

In your first example WrappedSSimple({1,2}) is calling the move-constructor of WrappedSSimple with the a temporary constructed via the user-defined constructor as argument.

You cannot replicate this behavior with a factory if the constructor is private, because the temporary object which needs access to the constructor is always created in the context of the caller.

You can also not generally forward untyped braces. The best you can do, if you are fine with restricting the types to be the same for each element of the brace, is to use a std::initializer_list or a reference to an array as parameter to make.

Indirect perfect forwarding via function pointer?

Background of the question is a mis-reading of Scott Meyer's article about forwarding references (called 'universal references' there).

The article gave the impression of such forwarding references existing as a separate type in parallel to ordinary l-value and r-value references. This is not the case, though, instead it is a hypothetical construct to explain special deduction rules that exist if template parameters serve as function parameter types.

As such a type does not exist in consequence it is not possible to declare a function pointer using that type, proving indirect perfect forwarding via function pointer impossible to realise.

c++ perfect forwarding with universal reference prevents type deduction?

In this case, you can add a default value to the U template parameter like

template<typename U = T>
void set(U&& a) {
a_=std::forward<U>(a);
}

and now if it can deduce the type, like with {} which has no type, then it will fall back t using T as the type for U.

std::move on const char* with perfect forwarding

What is std::move doing to a const char [12] and what are the potential side-effects?

The ordinary array-to-pointer implicit conversion, and none. Pointer types don't have move constructors or move assignment operators, so "moves" are copies (of the pointer the array decayed to).

Aside: I don't think your template does what you think it does. The pack T_Args... isn't deduced in when calling Foo::foo, so you don't have a universal reference, instead it's rvalue references.

Did you mean something like

template <typename... T_Args>
struct Foo
{
template <typename T_Func, typename... T_Args2>
void foo(T_Func func, T_Args2&&... args)
{
static_assert(std::is_constructible_v<T_Args, T_Args2> && ..., "Arguments must match parameters");
func(std::forward<T_Args2>(args)...);
}
};

Or possibly the even simpler

struct Foo
{
template <typename T_Func, typename... T_Args>
void foo(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
};

Perfect forwarding in the visitor pattern std::visit of C++

I can get rid of the perfect forwarding

Passing by a const reference would copy the lambdas instead of moving them.

It doesn't matter in your case, but it will matter if the lambdas capture things by value. It can hurt performance if the lambdas are expensive to copy, or cause a compilation error if the lambdas can't be copied (because they have non-copyable state, e.g. [x = std::make_unique<int>()](){}).

I can get rid of the ... the deduction guide.

If you pass by const reference, yes. But if you pass by a forwarding reference, no.

The constructor having an independent set of template parameters is one problem. Buf if you used the same template parameters (and inherited from std::decay_t<B>... instead), it still wouldn't work, because built-in deduction guides refuse to do perfect forwarding. The template argument deduction wouldn't work if you passed an lvalue.


A better question is why have a constructor at all. If you remove it, the type becomes an aggregate and everything continues to work as before.

It even lets you remove the deduction guide (except that Clang doesn't support that yet).

error perfect forwarding a lamda - discards qualifiers

To perfect forwarding use Functor&& func - not const Functor&& func

Perfect forwarding with a specific type

You can assert it:

template<typename T>
void push(T&& value)
{
static_assert(is_same_v<remove_reference_t<T>, value_type>);
// ...
}

You could alternatively use is_convertible instead of is_same so it works more naturally.



Related Topics



Leave a reply



Submit