分享

C++

 quasiceo 2014-01-05

C++为什么不支持元组作为函数返回值

用C++写程序的时候,最郁闷的就是我想要返回两个值,必须构造一个结构体来返回,或者一个通过返回值,另外一个通过引用传递参数。而其他语言比如python, erlang却支持。

在stack overflow也看到了这个问题
http:///questions/321068/returning-multiple-values-from-a-c-function

评论 (0) ? 分享 ? 链接 ? 2011-10-21 
10个答案

有二种方法可以解决

  1. 通过std::pair<one, two>来解决
  2. 更灵活的方式可以通过boost::Tuple
评论 (3) ? 链接 ? 2011-10-21
  • 1
    pair只能就解决两个返回值的问题。
    boost::Tuple,还要包含boost,并且还要构造对象和我自己声明一个结构体差不多。
    我原本的意思是能否从更深层次解释下,为什么可以非常简明的写法,一定要绕这么多弯。
    – 黄新颖 2011-10-21
  • 0
    Note: C++11 已经开始支持tuple了 – tlh1987 2012-10-20
  • 0
    @黄新颖 重点在于tuple是Python的内嵌类型,支持多长度的,所以可以直接作为多返回值使用;而C++没有最初内嵌的相应类型,所以需要通过库的拓展来实现。
    即便是C++11也是如此。
    – KPSN_Leo 2013-01-01

这个问题好大,python我不太懂还是从C++方面来说吧
C++最早引入对象模型的时候,语言的设计专家们已经在此基础上做了很多优化,但局部的编译生成的代码,也因为封装牺牲了空间和时间,设计者和编译器的构建人员为了C++的高效性,尽量减少语言在因为引入封装而带来的成本,最早的Stroustrup设计的简单对象模型发展到现在已经做了很多的优化,C++定义了对象的语法,但没有引入特定的对象作为语法的部分,我想还是考虑到效率的原因,函数的返回值在函数语义学上,构造一个返回语法支持的对象的函数,和一个返回值的函数,估计整个语法编译器都要重新设计。
每种语言都有自己的特长,应该是越高级的语言使用越方便,但因为方便而带来的是空间和时间的成本。

至尊宝
至尊宝
1044
编辑于 2011-10-21
评论 (4) ? 链接 ? 2011-10-21
  • 0
    构造一个返回语法支持的对象的函数,和一个返回值的函数,估计整个语法编译器都要重新设计。

    倒不会严重到这个程度,哈哈。语言本来就是越贴近使用者越好,就像C#原先不支持缺省参数一样,后来又在新版本中加入了缺省参数的支持。
    – 黄新颖 2011-10-21
  • 0
    这个和C/C++设计年代所需解决的问题有关系,也和c++不是纯面向对象,依赖简单的数据类似有关。 – 吴绩伟 2011-10-21
  • 0
    如果说为了效率而不增加多个返回值,那C++中的异常是个例证。 C++为了对异常的支持,性能和编译出来的代码都有不小的开销,我现在还一直认为异常是C++的一个败笔。 – 黄新颖 2011-10-21
  • 0
    @黄新颖 异常对于OOP来说是很重要的。不过C++的异常的确比较鸡肋,大家都适应C的用法了 – 灵剑2012 2012-09-14

1.想像中C语言如何支持函数有可变数量返回值
目前的编译器不支持,但是要给编译器加一种call的方式,让这种call支持返回多个返回值也是有可能的
比如我们增加一种 _ _klcall,这种函数把返回个数放在堆栈 - 0xc处,再之后是第一个返回值,之后是第二个。。。
这样的c语言代码

  
  1. _ _klcall int,int addsub(int a,int b)
  2. {
  3. return (a + b),(a -b)
  4. }
  5. int i,j = addsub(5,6);i == 11,j == -1

假设新的编译器支持这种 _ _klcall产生的汇编代码

  
  1. ;;函数
  2. ;;__klcall int,int addsub(int a,int b)
  3. ;;{
  4. ;;  return (a + b),(a -b)
  5. ;;}
  6. ;汇编代码
  7. push ebp
  8. mov ebp,esp
  9. push ebx
  10. mov eax,dword ptr ss:[ebp+0x8];参数1
  11. add eax,dword ptr ss:[ebp+0xC];参数1 + 参数2
  12. mov ebx,eax
  13. mov eax,dword ptr ss:[ebp+0x8];参数1
  14. add eax,dword ptr ss:[ebp+0xC];参数1 - 参数2
  15. ;add结果在ebx sub结果在eax
  16. ;现在开始输出。从 [ebp - 0x4]开始.因为[ebp]里保存的是调用前的ebp不能破坏
  17. mov dword ptr[ebp - 0x4], 2;先输出共返回两个参数
  18. mov dword ptr[ebp - 0x8], ebx;再输出返回add结果
  19. mov dword ptr[ebp - 0xc],eax;再输出返回sub结果
  20. pop ebx
  21. pop ebp
  22. retn 8

  23. ;;调用
  24. ;;int i,j = addsub(5,6);i == 11,j == -1
  25. ;;汇编代码
  26. push 0x5
  27. push 0x6
  28. call funAddSubAddres
  29. ;;这时esp指向0x5; [esp - 4]是上一个调用call的下一个地址,[esp - 8]是保留的ebp值,就是上面说不能破坏那个
  30. mov eax,[esp - 0xc]//上个调用共返回多少个函数
  31. mov [ebp - 0x4], [esp - 0x10]//add的结果 == 11
  32. mov [ebp - 0x8], [esp - 0x14]//sub的结果 == -1
  33. ;这时堆栈应该是这样的
  34. ;            0012FDD8   ffffffff;sub的结果-1
  35. ;            0012FDDC   0000000b;add的结果11
  36. ;            0012FDE0   00000002;返回两个返回值
  37. ;            0012FDE4  /0012FF08;  调用时的ebp
  38. ;            0012FDE8  |00411556;  返回到 call funAddSubAddres 的下一语句
  39. ;esp 指向这里->0012FDEC  |00000005;
  40. ;            0012FDF0  |00000006

2.从以上假想代码来看,要让函数支持多个返回值,也很简单,效率我也看不出有多大问题。如果把返回个数放在eax里返回,代码还可以再少一些。
3.所以我觉得之所以现代c函数不支持多个返回值,是因为编译器厂商没觉得有那必要。
如果想要这种 local a,b = subadd(5,6)这种这么方面的特性,那直接用脚本好了比如lua,python.
4.现在做游戏,特别是客户端游戏,如果不用脚本直接用c++,会让很多人把嘴张得大大的:怎么可能?其实还是淫者见淫

评论 (2) ? 链接 ? 2012-05-23
  • 0
    这个是有问题的,而且问题很严重。esp以上的部分绝对不可以存储数据。因为这时候如果有个中断进入的话,会立即覆盖掉esp以上的数据,这样运行结果就会出错。 – 灵剑2012 2012-09-14
  • 0
    local a,b = func();这种方式如果只是为了赋值的话,感觉类似语法糖,只是一个便捷的操作而已。。。 – 刘江 2012-10-10

主要还是没有设计这个编译器功能吧,
{min,max} = find_min_max(...)
相同的功能完全可以写成
void find_min_max(..., &min, &max);
或者用引用
void find_min_max(..., min, max);
没有必要为这种情况定义一种语法,只会导致混乱。

评论 (2) ? 链接 ? 2012-09-14
  • 0
    丝毫没有见到过Python或者Lua中因为引入多重返回值而变得混乱。 – KPSN_Leo 2013-01-01
  • 0
    @KPSN_Leo 基本语法的设计就不一样,Python大约也不能把程序全写在一行上吧。 – 灵剑2012 2013-01-04

终于想明白为什么C/C++只能返回一个值,而其他的脚本语言可以返回多个值了。c++在编译成汇编语言中,约定返回值用eax保存,并且只能保存一个值,除非改汇编指令。

  
  1. int add(int a, int b)  
  2. {  
  3.     return a + b;  
  4. }  
  5.  
  6. int process()  
  7. {  
  8.     int value = 0;  
  9.     __asm  
  10.     {  
  11.         push eax  
  12.         push 2  
  13.         push 3  
  14.         call add  
  15.         add esp, 8  
  16.         mov value, eax  
  17.         pop eax  
  18.     }  
  19.     return 1;  
  20. }
吴绩伟
吴绩伟
463
编辑于 2012-03-27
评论 (1) ? 链接 ? 2012-03-27
  • 0
    不不,你试试返回一个结构体,这个结构体会存在堆栈里面。这也是调用约定的一部分。 – 灵剑2012 2012-09-14

我觉得从汇编角度讨论这个问题可能不太好,这和C++语言是一种强类型语言有关。
在Python中,它是一种弱类型语言,一个元组中存放的可以是任何类型;而在C++中和元组最相近的vector中的元素必须是同一类型。
如果返回值是多个同类型值,比如是2个int数,那么C++可以用vector返回;但是如果是不同类型就不行了。
每个语言都有自己的长处,要善于选择使用语言。

评论 (0) ? 链接 ? 2012-07-06

我以前看python的书说
python 中的元组 貌似是防止 错误修改 才搞的
c++ 里边可以定义 不可修改的 结构或者类型

评论 (2) ? 链接 ? 2011-10-21
  • 1
    我想用元组的目的不是说想把他变成不可修改,我比较好奇为什么C++不能让我写一个这样的函数。
    -------------------------------------------
    int min, max;
    min, max = find_min_max( const list& l );
    -------------------------------------------
    int err_no;
    string err_msg;
    errno, err_msg = last_error();
    – 黄新颖 2011-10-21
  • 0
    用stl中的map啊 – Thomas 2011-10-21

C++是通过把返回值放置在EAX里面来获取返回值的,所以只能存放一个值(包括指针等等)

评论 (0) ? 链接 ? 2012-08-27

pair很好 如果要返回多个参数boost里有很多方案可用

评论 (2) ? 链接 ? 2011-10-21
  • 1
    题目是Why ,而不是How – 黄新颖 2011-10-21
  • 0
    我的理解是C++就是通过那些模板结构体类型支持了元组返回值。
    只是他的返回值要定义的,不像动态语言那样。
    没准哪年c++新标准,就加上 return xxx,xxx
    – 孙继峥 2011-10-21

你的意思是语法上想这么写:

  
  1. a, b, c = f();

这的确是一种语法上的便利性。实际上函数中最终有一个return,返回一个元组“对象”。它不是数组这种简单的“同一的多个对象”,所以这个对象能存放多种类型的对象,那么C++就必须为没个对象内置"运行期识别"的类型识别,包括内置对象,你觉得可能吗。这是一个高级对象,C++语言设计目的之一就是只引入基本的内置类型,用户需要的高级类型需要自己构造。

评论 (0) ? 链接 ? 2012-10-19

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多