jjcasmar
jjcasmar

Reputation: 1665

std::apply emplace_back with a parameter pack

I am trying to emplace a new object into a container inside a template function. Since the template function receives an std::tuple with the arguments needed to create the object, I am using std::apply and a fold expression to unpack the tuple values. However, it doesn't compile

My code is equivalent to

#include <vector>

struct S
{
    S(float restAngle, bool useIt) 
      : restAngle(restAngle)
      , useIt(useIt)
    {}
    float restAngle;
    bool useIt;
};


int main()
{
    std::tuple t(0.0F, false);
    std::vector<S> ss;
    std::apply([&ss](auto ...data) {ss.emplace_back((data,...));}, t);
}

and it fails with the following compilation error in gcc 13

n file included from /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/x86_64-linux-gnu/bits/c++allocator.h:33,
                 from /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/allocator.h:46,
                 from /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/vector:63,
                 from <source>:1:
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/new_allocator.h: In instantiation of 'void std::__new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {bool&}; _Tp = S]':
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/alloc_traits.h:537:17:   required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = S; _Args = {bool&}; _Tp = S; allocator_type = std::allocator<S>]'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/vector.tcc:117:30:   required from 'std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {bool&}; _Tp = S; _Alloc = std::allocator<S>; reference = S&]'
<source>:18:52:   required from 'main()::<lambda(auto:1 ...)> [with auto:1 = {float, bool}]'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:2558:26:   required by substitution of 'template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = main()::<lambda(auto:1 ...)>; _Args = {float&, bool&}]'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:2569:55:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:161:35:   recursively required by substitution of 'template<class _Result, class _Ret> struct std::__is_invocable_impl<_Result, _Ret, true, std::__void_t<typename _CTp::type> > [with _Result = std::__invoke_result<main()::<lambda(auto:1 ...)>, float&, bool&>; _Ret = void]'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:161:35:   required by substitution of 'template<class ... _Bn> std::__detail::__first_t<std::integral_constant<bool, true>, typename std::enable_if<(bool)(_Bn::value), void>::type ...> std::__detail::__and_fn(int) [with _Bn = {std::__is_invocable_impl<std::__invoke_result<main()::<lambda(auto:1 ...)>, float&, bool&>, void, true, void>, std::__call_is_nothrow<std::__invoke_result<main()::<lambda(auto:1 ...)>, float&, bool&>, main()::<lambda(auto:1 ...)>, float&, bool&>}]'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:177:42:   required from 'struct std::__and_<std::__is_invocable_impl<std::__invoke_result<main()::<lambda(auto:1 ...)>, float&, bool&>, void, true, void>, std::__call_is_nothrow<std::__invoke_result<main()::<lambda(auto:1 ...)>, float&, bool&>, main()::<lambda(auto:1 ...)>, float&, bool&> >'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/type_traits:3103:12:   required from 'struct std::is_nothrow_invocable<main()::<lambda(auto:1 ...)>, float&, bool&>'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/tuple:2272:31:   required from 'constexpr const bool std::__unpack_std_tuple<template<class _Fn, class ... _ArgTypes> struct std::is_nothrow_invocable, main()::<lambda(auto:1 ...)>, std::tuple<float, bool>&>'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/tuple:2295:14:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = main()::<lambda(auto:1 ...)>; _Tuple = tuple<float, bool>&]'
<source>:18:15:   required from here
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/new_allocator.h:187:11: error: no matching function for call to 'S::S(bool&)'
  187 |         { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
      |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:5:5: note: candidate: 'S::S(float, bool)'
    5 |     S(float restAngle, bool useIt)
      |     ^
<source>:5:5: note:   candidate expects 2 arguments, 1 provided
<source>:3:8: note: candidate: 'constexpr S::S(const S&)'
    3 | struct S
      |        ^
<source>:3:8: note:   no known conversion for argument 1 from 'bool' to 'const S&'
<source>:3:8: note: candidate: 'constexpr S::S(S&&)'
<source>:3:8: note:   no known conversion for argument 1 from 'bool' to 'S&&'

I can't find what is wrong with the code. Apparently is trying to call the emplace_back with just the bool arguments, which I don't understand. What is wrong with the code?

Upvotes: 0

Views: 115

Answers (1)

NathanOliver
NathanOliver

Reputation: 180594

You have the syntax a little off. In

ss.emplace_back((data,...))

You are using a fold expression of the comma operator on the pack of data so it expands out to

ss.emplace_back((data1, data2, ..., dataN))

and since it is the comma operator only dataN is returned from the inner parentheses which in your case is the bool object of the tuple. To get all of the pack in the call to emplace_back you just need

ss.emplace_back(data...)

which expands to

ss.emplace_back(data1, data2, ..., dataN)

which is how the call to emplace_back should look like.

Upvotes: 3

Related Questions