g++ 编译returning reference to local temporary object 但是运行依然得到预期结果? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chuanqirenwu
V2EX    C++

g++ 编译returning reference to local temporary object 但是运行依然得到预期结果?

  •  
  •   chuanqirenwu 2022-04-04 16:35:40 +08:00 2638 次点击
    这是一个创建于 1351 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码来自 C++ templates 第二版:

    #include <cstring> #include <iostream> using std::cout; using std::endl; // maximum of two values of any type (call-by-reference) template <typename T> T const &max(T const &a, T const &b) { cout << "T const &max(T const &a, T const &b) called" << endl; return b < a ? a : b; } // maximum of two C-strings (call-by-value) char const *max(char const *a, char const *b) { cout << "char const *max(char const *a, char const *b) called" << endl; cout << a << endl; cout << b << endl; return std::strcmp(b, a) < 0 ? a : b; } // maximum of three values of any type (call-by-reference) template <typename T> T const &max(T const &a, T const &b, T const &c) { return max(max(a, b), c); // error if max(a,b) uses call-by-value } int main() { char const *s1 = "frederic"; char const *s2 = "anica"; char const *s3 = "lucas"; auto m2 = ::max(s1, s2, s3); // run-time ERROR //!!! m2 已经是一个 dangling reference ,但是仍然得到预期结果? cout << m2 << endl; } 

    不理解的地方在最后一行,m2 已经是一个 dangling reference ,但是为什么输出仍然得到预期结果?

    编译使用的是 g++:

    $ g++ --version Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1 Apple clang version 12.0.0 (clang-1200.0.31.1) Target: x86_64-apple-darwin21.1.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin 

    编译输出:

    $ g++ test2.cpp -o test2 -std=c++11 test2.cpp:28:12: warning: returning reference to local temporary object [-Wreturn-stack-address] return max(max(a, b), c); // error if max(a,b) uses call-by-value ^~~~~~~~~~~~~~~~~ test2.cpp:36:17: note: in instantiation of function template specialization 'max<const char *>' requested here auto m2 = ::max(s1, s2, s3); // run-time ERROR ^ 1 warning generated. 

    运行结果:

    char const *max(char const *a, char const *b) called frederic anica char const *max(char const *a, char const *b) called frederic lucas lucas 
    14 条回复    2022-04-09 11:16:22 +08:00
    nightwitch
        1
    nightwitch  
       2022-04-04 16:38:47 +08:00
    undefined behaviour
    可能出现任意的结果
    chuanqirenwu
        2
    chuanqirenwu  
    OP
       2022-04-04 16:43:15 +08:00
    @nightwitch 意思是 g++ 下将这个 undefined behavior 定义成了最接近预期结果的 behavior 吗?从运行结果来看,就是得到的预期的输出:lucas
    nightwitch
        3
    nightwitch  
       2022-04-04 17:00:36 +08:00
    ub 的结果是不能预测的。
    也许你在多写几行或者换个编译参数他结果就变了。
    不用去深究它,也不要依赖你观察到的表现
    macrorules
        4
    macrorules  
       2022-04-04 17:04:19 +08:00
    @nightwitch 怎么看出是 UB 啊?
    nightwitch
        5
    nightwitch  
       2022-04-04 17:08:25 +08:00
    @macrorules
    https://en.cppreference.com/w/cpp/language/reference
    翻到最下面 “Accessing such a reference is undefined behavior. ”
    macrorules
        6
    macrorules  
       2022-04-04 17:08:41 +08:00
    如果你把 lucas 改成 eucas ,就会报错,因为 max(a, b) 的结果是一个临时变量,调用栈收缩之后就没了
    chuanqirenwu
        7
    chuanqirenwu  
    OP
       2022-04-04 17:21:50 +08:00
    @macrorules max(max(a, b), c) 整个返回应该都是临时变量,因此跟参数的值应该没有关系。
    icylogic
        8
    icylogic  
       2022-04-04 18:19:01 +08:00
    https://godbolt.org/z/PbMf5Ps8K

    a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such return statement always returns a dangling reference.
    yanqiyu
        9
    yanqiyu  
       2022-04-04 18:43:51 +08:00   1
    就算返回了临时变量的引用,但是也不意味着临时变量引用对应的地址会挪作他用,一般来说紧接着就用了而未进行压栈也大概率展示用不到
    phiysng
        10
    phiysng  
       2022-04-04 23:13:40 +08:00   1
    @chuanqirenwu 未定义就是真的未定义,不存在`定义成了最接近预期结果的 behavior `
    FrankHB
        11
    FrankHB  
    &bsp;  2022-04-08 18:43:55 +08:00
    能运行也是 UB 的一种。
    但是应该强调,返回临时变量的引用不一定就 UB ,这里只有限定返回局部自动对象的引用时才是。否则,这会显著干扰一些问题的理解,例如:
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67795
    FrankHB
        12
    FrankHB  
       2022-04-08 19:19:22 +08:00
    @FrankHB 修正:返回非静态存储期的临时对象的引用。
    和 @nightwitch 指出的来源一样,返回自动对象的引用是悬空引用。这里的自动对象是一个被声明的局部变量。
    但是仔细看了下,OP 的代码中并不是这种情况。
    问题出在 max(max(a, b), c)返回的是 const char*类型的右值,通过 temporary materialization conversion 初始化一个临时对象并初始化 T const&[T=const char*]类型的返回值,在 return 外临时对象被销毁,所以返回悬空引用。
    注意这不涉及 C++意义上的变量。使用 g++时,中文警告[-Wreturn-local-addr]会把它叫做“临时变量”,这是技术上错的;而原文 returning reference to temporary 是对的,虽然过时( ISO C++17 前就直接叫 temporary ,不叫 temporary object )。
    注意这里实际上用的是软链接过去的 Apple clang++,警告的选项不一样。
    因为不是被声明变量,所以说 local (指的是作用域)也是技术上错的。
    OP 的注释也是错的,悬空引用是=右边的部分;而被声明的变量 m2 就不是引用,因为用的是 auto 而不是 auto&。
    chuanqirenwu
        13
    chuanqirenwu  
    OP
       2022-04-08 22:07:10 +08:00
    @FrankHB 谢谢!还是不理解,为什么说 m2 不是引用呢?返回类型不是 `T const &` 吗?
    FrankHB
        14
    FrankHB  
       2022-04-09 11:16:22 +08:00
    @chuanqirenwu 声明的类型通过初值符的类型推断,不保证相同。和在函数模板的参数列表里写不带&的参数类型规则相同,占位符不会被推断为引用类型,于是声明的 m2 不是引用类型的变量。如果要保留引用,可以 auto&或 auto&&之类。
    http://www.eel.is/c++draft/dcl.spec.auto#dcl.type.auto.deduct-3
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5541 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 06:27 PVG 14:27 LAX 22:27 JFK 01:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86