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

为何std::views::transform组合调用会产生异常输出?

问题:为何View0的输出与Created和View1不一致?

以下是测试代码:

#include <iostream>
#include <vector>
#include <ranges>

struct S
{
    float x = 150.f;
    S f() const
    {
        return *this;
    }
};

int main()
{
    std::vector<S> vec{
        { 1.f },
        { 2.f }
    };

    std::cout << "\nCreated\n";

    for ( const auto& l : vec )
    {
        std::cout << l.f().x << ' ';
    }
    std::cout << "\nView0\n";
    
    for ( float t : vec
                    | std::views::transform( &S::f )
                    | std::views::transform( &S::x )
        )
    {
        std::cout << t << ' ';
    }
    std::cout << "\nView1\n";

    auto view1
        = vec
        | std::views::transform( &S::f )
        | std::views::transform( [] ( const S& l ) { return l.x; } );

    for ( float t : view1 )
    {
        std::cout << t << ' ';
    }
}

开启优化的Clang和GCC运行后输出:

Created
1 2 
View0
0 0 
View1
1 2

GCC开启-Wall -Wextra -pedantic-errors时的警告:

<source>:33:52: warning: using a dangling pointer to an unnamed temporary [-Wdangling-pointer=]
   33 |                     | std::views::transform( &S::x )
      |                                                    ^
In file included from <source>:3:
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/ranges:1949:54: note: unnamed temporary defined here
 1949 |           { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
<source>:33:52: warning: '<unnamed>.S::x' may be used uninitialized [-Wmaybe-uninitialized]
   33 |                     | std::views::transform( &S::x )
      |                                                    ^
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/ranges:1949:54: note: '<anonymous>' declared here
 1949 |           { return std::__invoke(*_M_parent->_M_fun, *_M_current); }

原因分析

View0的问题:临时对象悬空引发未定义行为

  1. 第一个std::views::transform(&S::f)会对容器中的每个S对象调用f(),而f()的返回类型是S(值返回),因此每次调用都会生成一个临时S对象。
  2. 第二个std::views::transform(&S::x)使用成员指针&S::x来访问成员。此时transform内部的逻辑是先获取临时对象的地址,再通过成员指针读取x——但这个临时对象在第一个transform的调用完成后就会被立即销毁,导致后续访问的是悬空指针指向的内存,这属于C++标准明确的未定义行为,输出结果完全不可预测(可能是0、垃圾值甚至程序崩溃)。

为什么Created和View1能正常工作?

  • Created区块:循环中l.f().x是直接在表达式中访问临时对象的成员,C++规则会将临时对象的生命周期延长到整个表达式结束(即std::cout调用完成),因此能正确读取到值。
  • View1区块:第二个transform使用lambda表达式[] (const S& l) { return l.x; },lambda接收的是临时对象的常量引用。根据C++规则,绑定到常量引用的临时对象生命周期会延长到引用的生命周期结束(即lambda函数调用期间),因此可以安全访问l.x,不会出现悬空问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 07:23:20