c++tuplesunique-ptr

How can I wrap std::make_unique in std::apply?


The following does not compile:

#include <memory>
#include <tuple>
#include <utility>

int main()
{
    std::apply(std::make_unique<int>, std::make_tuple(1));
    
    return 0;
}

Error message:

In file included from /usr/include/c++/11/bits/unique_ptr.h:37,
                 from /usr/include/c++/11/memory:76,
                 from main.cpp:1:
/usr/include/c++/11/tuple: In instantiation of ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::unique_ptr<int, std::default_delete<int> > (&)(); _Tuple = std::tuple<int>; long unsigned int ..._Idx = {0}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0>]’:
/usr/include/c++/11/tuple:1869:31:   required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::unique_ptr<int, std::default_delete<int> > (&)(); _Tuple = std::tuple<int>]’
main.cpp:7:15:   required from here
/usr/include/c++/11/tuple:1858:27: error: no matching function for call to ‘__invoke(std::unique_ptr > (&)(), std::__tuple_element_t<0, std::tuple >)’
 1858 |       return std::__invoke(std::forward<_Fn>(__f),
      |              ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
 1859 |                            std::get<_Idx>(std::forward<_Tuple>(__t))...);
      |                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/11/tuple:41,
                 from /usr/include/c++/11/bits/unique_ptr.h:37,
                 from /usr/include/c++/11/memory:76,
                 from main.cpp:1:
/usr/include/c++/11/bits/invoke.h:90:5: note: candidate: ‘template constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)’
   90 |     __invoke(_Callable&& __fn, _Args&&... __args)
      |     ^~~~~~~~
/usr/include/c++/11/bits/invoke.h:90:5: note:   template argument deduction/substitution failed:
/usr/include/c++/11/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = std::unique_ptr<int, std::default_delete<int> > (&)(); _Args = {int}]’:
/usr/include/c++/11/tuple:1858:27:   required from ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::unique_ptr<int, std::default_delete<int> > (&)(); _Tuple = std::tuple<int>; long unsigned int ..._Idx = {0}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0>]’
/usr/include/c++/11/tuple:1869:31:   required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::unique_ptr<int, std::default_delete<int> > (&)(); _Tuple = std::tuple<int>]’
main.cpp:7:15:   required from here
/usr/include/c++/11/bits/invoke.h:90:5: error: no type named ‘type’ in ‘struct std::__invoke_result > (&)(), int>’

How can this be fixed?


Solution

  • C++17 and earlier

    std::make_unique<int> has the type std::unique_ptr<int>() which is a function that returns a std::unique_ptr<int> and takes no parameters. That is not what you want since you are passing a value to the function.

    What you need is std::make_unique<int, int> which has the type std::unique_ptr<int>(int) which is what you want. The first template parameter is for the type of the unique_ptr and each additional type is for the argument types of the make function.

    C++20+

    Since C++20 make_unique is not an addressable function so you are no longer allowed to use this technique in portable code. Instead you'll need to leverage a lambda for a level of indirection like

    std::apply([](auto&& var){ return std::make_unique<int>(decltype(var)(var)); }, std::make_tuple(1));