You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用std::apply结合emplace_back展开参数包编译失败问题排查

问题:使用std::apply展开tuple调用emplace_back编译失败

尝试在模板函数中向容器内emplace新对象,函数接收包含对象构造所需参数的std::tuple,因此使用std::apply和折叠表达式来展开tuple中的值,但代码无法编译。等价代码如下:

#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);
}

在GCC 13中编译时出现如下错误:

In 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&&'

问题原因与修复

原因

你写的(data,...)是逗号表达式,它会依次计算每个data表达式,但最终只返回最后一个表达式的值(也就是tuple里的最后一个元素false)。这就导致emplace_back实际只收到了一个bool参数,完全匹配不上需要float和bool两个参数的S构造函数,所以编译器报错。

修复方法

不需要使用折叠表达式,直接把参数包展开传递给emplace_back即可——emplace_back本身就是可变参数模板,支持直接接收多个参数并转发给对象的构造函数。只需要把(data,...)改成data...:

#include <vector>
#include <tuple> // 注意:原代码遗漏了tuple头文件,标准中必须显式包含

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);
}

额外说明

  • 原代码遗漏了<tuple>头文件:虽然部分编译器会通过其他标准头间接引入它,但按照C++标准,使用std::tuple和std::apply必须显式包含<tuple>,否则代码不具备可移植性。
  • 折叠表达式在这里完全多余:std::apply已经把tuple的元素展开成了参数包data...,直接传递给emplace_back就会把每个元素作为独立参数转发,正好匹配S的构造函数。

内容的提问来源于stack exchange,提问作者jjcasmar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:57:04