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

如何使用折叠表达式实现std::tuple逐元素赋值并解决只读报错

问题描述

我定义了包装可变参数std::tuple的自定义类型Foo,目标是优先使用折叠表达式(而非std::index_sequence等相对繁琐的写法)实现加赋值运算符,对两个实例内tuple的元素逐对执行加赋值操作,预期运行输出为5 7 9。
原实现代码如下:

#include <iostream>
#include <tuple>

template <typename ...Args>
struct Foo {
    std::tuple<Args...> t;

    Foo(Args&&... a)
        : t{ std::forward<Args>(a)... }
    { }

    Foo& operator +=(const Foo& f) {
        std::apply([&](auto&&... ts) {
            std::apply([...ts = std::forward<decltype(ts)>(ts)](auto&&... fts) {
                ((ts += fts), ...);
            }, f.t);
        }, t);
        return *this;
    }

    friend std::ostream& operator <<(std::ostream& os, const Foo& f) {
        std::apply([&](auto&&... ts) {
            ((os << ts << ' '), ...);
        }, f.t);
        return os;
    }
};

int main() {
    Foo goy{ 1, 2, 3 };
    Foo bar{ 4, 5, 6 };
    goy += bar;
    std::cout << goy << std::endl; // expect 5 7 9 as output
}

该代码无法通过编译:GCC抛出如下编译错误:

error: assignment of read-only variable 'ts#0'
   15 |                 ((ts += fts), ...);
      |                  ~~~~^~~~~~~

Clang则触发内部编译器崩溃。
待确认问题:

  • 上述代码是否不符合C++标准?
  • 该问题是否为编译器bug?
  • 是否存在优雅的修复方案?
问题原因
  • 代码本身存在逻辑+语法双重错误,不符合C++标准:
    1. 内层lambda使用按值初始化捕获...ts = std::forward<decltype(ts)>(ts),会将外层std::apply拿到的当前tuple元素拷贝一份存储在lambda内部,即使修改成功也只会改动副本,完全无法影响原tuple的元素,逻辑不成立。
    2. C++标准规定lambda的调用运算符默认带有const限定,按值捕获的变量在lambda内部默认是只读的,直接对其执行赋值操作会触发const限定违规,这就是GCC报错的直接原因。
  • Clang触发内部崩溃属于旧版本Clang对C++20参数包捕获场景的实现bug,并非标准规定的行为。
修复方案

优先使用折叠表达式、不依赖std::index_sequence的兼容C++20的修复写法非常简单,只需要将内层lambda的按值捕获改为按引用捕获参数包即可,避免拷贝同时获得修改权限:

Foo& operator +=(const Foo& f) {
    std::apply([&](auto&&... ts) {
        std::apply([&ts...](auto&&... fts) {
            ((ts += fts), ...);
        }, f.t);
    }, t);
    return *this;
}

该写法完全符合标准:两个同类型Foo实例的tuple长度一致,折叠表达式会按顺序将对应位置的元素两两配对执行加赋值,没有多余拷贝,逻辑正确。
如果使用C++23及更新版本,还可以用更简洁的多tuplestd::apply写法,连嵌套lambda都不需要:

Foo& operator +=(const Foo& f) {
    std::apply([](auto&... self_elem, const auto&... other_elem) {
        ((self_elem += other_elem), ...);
    }, t, f.t);
    return *this;
}

修复后代码运行即可输出预期结果5 7 9。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:00:45