C++ 模板重载问题请教

2018-11-22 17:15:24 +08:00
 hackpro

原文解释是说 c-strings 这种重载返回了一个局部变量的引用,便随着 stack unwinding 被回收了。

但是没有明白为什么这段代码会产生一个局部变量的引用,恳请 v 站各位大侠帮忙指点,多谢~

return max (max(a,b), c); becomes a run-time error because for C-strings, max(a,b) creates a new, temporary local value that is returned by reference, but that temporary value expires as soon as the return statement is complete, leaving main() with a dangling reference. Unfortunately, the error is quite subtle and may not manifest itself in all cases.

Note, in contrast, that the first call to max() in main() doesn ’ t suffer from the same issue. There temporaries are created for the arguments (7, 42, and 68), but those temporaries are created in main() where they persist until the statement is done.

代码如下

#include <cstring>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{
	return b < a ? a : b;
}

// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{
	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 ()
{
	auto m1 = ::max(7, 42, 68); // OK
	char const* s1 = "frederic";
	char const* s2 = "anica";
	char const* s3 = "lucas";
	auto m2 = ::max(s1, s2, s3); //run-time ERROR
}
3281 次点击
所在节点    C++
25 条回复
wutiantong
2018-11-23 11:37:42 +08:00
正如上面各位大佬所说,原题代码的一个可行的修改方案是把第二个 max 的函数声明改为:

char const* const& max (char const* const& a, char const* const& b)

(虽然这种代码其实很蠢,逃?
hackpro
2018-11-23 12:07:45 +08:00
@arzterk #16
其实我想问的是如果编译器不进行优化的话 被调函数的参数 /返回值是怎么和原调函数进行数据交换的
PS: 非常感谢您推荐的书

@GeruzoniAnsasu #18
感谢大佬

@wutiantong #20
非常感谢您的回复
按照您说的如果编译器不进行优化的话 第二步返回值由 x+1 构建 这个返回值是存在被调函数的栈帧中吗
另外对于一般的函数调用如果不考虑编译器优化的话,参数 /返回地址 /返回值在栈帧中的布局大概是什么样的
我从参考链接中了解到的顺序为:参数 /返回地址 (请见参考链接第 13 页)
https://www.cs.bham.ac.uk/~hxt/2015/c-plus-plus/stack.pdf
wutiantong
2018-11-23 13:21:47 +08:00
@hackpro

其实 C++的*标准*并不涉及内存上的堆栈问题(包括寄存器),*标准*不关注这些东西,需要关注这些东西的是具有不同*实现*的编译器。
但是*标准*确实会关注对象的生命周期,在*实现*中对象的生命周期与堆栈之间有紧密的关联。

“这个返回值是存在被调函数的栈帧中吗?” 答:这个返回值是一个临时变量。

换个角度来说,我写 C++好几年了,我只关注*标准*定义了哪些概念哪些行为,这对我而言就足够了。
我不会特意去关注具体*实现*的内部细节,除非它产生的行为与*标准*产生了偏差。

所以我也不关心“函数调用时栈帧的实际布局是什么样子的”,这个问题对我而言既无意义也无帮助。
wutiantong
2018-11-23 13:23:34 +08:00
@hackpro 关于生命周期和临时变量,请参考: https://en.cppreference.com/w/cpp/language/lifetime
hackpro
2018-11-23 15:17:17 +08:00
@wutiantong 非常感谢大佬的指点

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/510475

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX