免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7211 | 回复: 10
打印 上一主题 下一主题

[C++] 名字空间、重载候选与lambda。。。 这是g++的bug吗。。。 [复制链接]

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-11-17 19:58 |显示全部楼层 |倒序浏览
30可用积分
简化后的代码大概是这样:
  1. #include <assert.h>

  2. template<typename T> int f(T const&) { return 0; }
  3. namespace ns {
  4. template<typename T> int f(T const&) { return 1; } }

  5. int main()
  6. {
  7.       using ::ns::f;
  8.       assert(1==::ns::f([]{}));
  9.       assert(1==f(0));
  10. #ifndef NO_AMBIGUOUS
  11.       assert(1==f([]{}));
  12. #endif
  13. }
复制代码
全局和ns名字空间下都有f函数模板。实际代码已经全部砍掉,只留下不同的返回值以方便后面区别。
然后在main里面using ::ns::f 。。。

::ns::f([]{}) 和 f(0) 调用的都是::ns::f。 问题出在f([]{})上。。。
用clang编译调用的也是::ns::f。 而g++编译就报错了。。。

  1. overload_lambda_ambiguous.cpp: In function 'int main()':
  2. overload_lambda_ambiguous.cpp:14:7: error: call of overloaded 'f(main()::<lambda()>)' is ambiguous
  3. overload_lambda_ambiguous.cpp:14:7: note: candidates are:
  4. overload_lambda_ambiguous.cpp:6:26: note: int ns::f(const T&) [with T = main()::<lambda()>]
  5. overload_lambda_ambiguous.cpp:4:26: note: int f(const T&) [with T = main()::<lambda()>]
复制代码
从报错的最后两行来看, 两个f都在重载候选里, 并且都能匹配, 于是ambiguous。
那为什么f(0)又只选择了::ns::f。。。
g++ -DNO_AMBIGUOUS 去掉那行后就正常了。

我用的g++版本比较老。。。 但在某个提供在线编译的网站上用gcc 5.1编译还是有这样的问题。。。 从4.6.3到5.1。。。 都不修复吗。。。

SO...
1. using ::ns::f; 后会hide ::f 对么?
2. 如果对, 那为什么用lambda作为实际参数调用时又没hide? 这是g++的bug吗?
3. 如果不对, f(0) 为什么只选中了::ns::f?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
2 [报告]
发表于 2015-11-18 19:14 |显示全部楼层
回复 2# fender0107401
没有吧? 除了[]{}其他都是C++98的东西吧。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
3 [报告]
发表于 2015-11-18 19:16 |显示全部楼层
回复 3# lost_templar
写e文比较费时。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
4 [报告]
发表于 2015-11-18 19:38 |显示全部楼层
本帖最后由 OwnWaterloo 于 2015-11-18 19:40 编辑

回复 4# windoze

目前还好。。。 反正是写测试时碰到的。。。  即使绕不过去大不了不测了

大概情况是这样。。。

1. 有几个宏。。。
MODULE_NS
MODULE_NS_ENTER
MODULE_NS_LEAVE
如果MODULE_NS没有定义就有一个默认值

2. 后续代码在合适的地方会用这些宏
MODULE_NS_ENTER
... // 这里面定义的一些宏,因为不知道它们会在何处展开,所以会使用 MODULE_NS::detail::some_thing 之类的
MODULE_NS_LEAVE

如果对默认放的名字空间不满意, 就在包含module.hpp前定义这几个宏,把内容放到别的名字空间里去。

3. 测试使用这几个宏

本来应该是分几个cpp单独测的。

  1. // test_ns_global.cpp
  2. #define MODULE_NS
  3. #define MODULE_NS_ENTER
  4. #define MODULE_NS_LEAVE
  5. #include "module.hpp"
复制代码

  1. // test_ns.cpp
  2. #define MODULE_NS ::ns
  3. #define MODULE_NS_ENTER namespace ns {
  4. #define MODULE_NS_LEAVE }
  5. #include "module.hpp"
复制代码

  1. // test_ns_nested.cpp
  2. #define MODULE_NS ::ns::util
  3. #define MODULE_NS_ENTER namespace ns { namespace util {
  4. #define MODULE_NS_LEAVE } }
  5. #include "module.hpp"
复制代码
test_ns_global.cpp, test_ns.cpp, test_ns_nested.cpp 里面显然还要包含一个真正写测试的文件, 不然重复写这么多次不科学。

我觉得这样文件太多很烦。。。 就都放在一个test_ns.cpp里了。

  1. // test_ns.cpp
  2. #ifndef ONLEAVE_TEST_NS_NAME
  3. #include <assert.h>
  4. #include <typeinfo>
  5. // include ...

  6. // 包含自己3次。。。
  7. #define MODULE_NS
  8. #define MODULE_NS_ENTER
  9. #define MODULE_NS_LEAVE
  10. #define MODULE_TEST_NS_NAME global
  11. #include "test_ns.cpp"

  12. #define MODULE_NS ::ns
  13. #define MODULE_NS_ENTER namespace ns {
  14. #define MODULE_NS_LEAVE }
  15. #define MODULE_TEST_NS_NAME ns
  16. #include "test_ns.cpp"

  17. #define MODULE_NS ::ns::util
  18. #define MODULE_NS_ENTER namespace ns { namespace util {
  19. #define MODULE_NS_LEAVE } }
  20. #define MODULE_TEST_NS_NAME nested
  21. #include "test_ns.cpp"

  22. int main() {}
  23. #else /* defined(MODULE_TEST_NS_NAME)*/
  24. // 实际测试用的代码
  25. #endif
复制代码
然后就中招了。。。

如果gcc不报错,但选错了名字空间的话, 测试应该全都能通过(毕竟所有名字空间里的功能都是一样的。。。)。。。
但有测试里面有输出typeid(x).name(), 所以仔细看还是能看出来。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
5 [报告]
发表于 2015-11-18 19:46 |显示全部楼层
本帖最后由 OwnWaterloo 于 2015-11-18 19:47 编辑

回复 4# windoze

module.hpp里面肯定会用C++11的东西, 所以用户也肯定要用C++11, 所以。。。
相比:

  1. #define MODULE_NS ::my::favorite::ns
  2. #define MODULE_NS_ENTER namespace my { namespace favorite { namespace ns {
  3. #define MODULE_NS_LEAVE }}}
  4. #include "module.hpp"
复制代码
用C++11新加的这个(namespace alias?):

  1. #include "module.hpp"
  2. namespace favorite = a::default::explict::and::long::ns;
复制代码
会更方便点?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
6 [报告]
发表于 2015-11-18 19:50 |显示全部楼层
回复 4# windoze
用了一阵C++11。。。 一个字爽。。。 两个字好爽。。。 三个字十分爽。。。
当然也遇见不少坑。。。 绝大部分应该都是我本地环境太搓。。。 除了这个疑似bug。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
7 [报告]
发表于 2015-11-19 00:04 |显示全部楼层
回复 5# VIP_fuck

看过喵叔的回复我越发感觉这就是个bug了

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
8 [报告]
发表于 2015-11-19 22:12 |显示全部楼层
本帖最后由 OwnWaterloo 于 2015-11-19 22:13 编辑

回复 13# fender0107401

这个是匿名函数。

  1. #include <assert.h>
  2. int main(int argc, char* argv[])
  3. {
  4.       auto add = [](int a, int b) { return a+b; };
  5.       assert(add(1,2)==3);
  6. }
复制代码
有参数列表后会不会熟悉点了。。。


如果不需要参数就可以省略括号

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(int argc, char* argv[])
  4. {
  5.       atexit([](void){ printf("(void)\n");  });
  6.       atexit([](    ){ printf("()\n");      });
  7.       atexit([]      { printf("omitted\n"); });
  8. }
复制代码
[]{}就是一个接受0个参数并且不做任何事的函数。。。


因为函数可以随处写了,于是就有一个写在顶层的函数不具备的功能。 []和那个功能有关。。。

写在顶层的函数,除了参数之外, 只要有相应的声明还可以访问其他的函数或变量。 这些函数或变量的生命周期都差不多是整个程序执行期间。

而匿名函数除了参数和静态生命周期的函数变量之外, 可以访问一些其他函数的局部变量。
比如前面的argc和argv, 还有add也是main的局部变量。

既然是局部变量, 它的生命周期就会更短。 并且c++也没有垃圾回收, 于是对局部变量的访问就保守一点。。。
[]中就是用来显式地控制函数体{}中可以访问哪些非参数中的变量, 以及以什么形式访问。。。。

[]不允许访问任何非参数非静态生命周期的东西。 除此之外最简单的形式就是[=]和[&]:
  1. #include <assert.h>
  2. #include <stdio.h>
  3. int main(int argc, char* argv[])
  4. {
  5.       int v = 0;
  6.       auto byval = [=]/* () -> const int* */{ return &v; };
  7.       assert(byval() != &v);
  8.       int v1 = *byval();    assert(v1==0);
  9.       v = 1;
  10.       int v2 = *byval();    assert(v2==0);

  11.       int r = 0;
  12.       auto byref = [&]/* () -> int* */{ return &r; };
  13.       assert(byref() == &r);
  14.       int r1 = *byref();    assert(r1==0);
  15.       r = 1;
  16.       int r2 = *byref();    assert(r2==1);
  17. }
复制代码
byval 以[=]的形式访问(捕获)了不是参数也不是静态变量,而是main的局部变量的v。 [=]允许随意访问。。。 要访问argc,argv也是可以的。
byval创建之后, 它里面的v是外面的v的复制([=]中等号=借用了赋值的含义)。
byref 以[&]的形式捕获了r。 两者是同一个整数变量。 ([&]中的&借用了取地址的含义)。
注释里面是返回类型的写法, 在编译器可以推导出来时可以省略。

总之, 这和其他语言里面的匿名函数以及写在顶层的函数没有太大的区别。。。   参数列表,函数体,返回类型(位置稍微有点变动)。。。
区别最大的应该就是[]了。
其他大部分语言都有垃圾回收, 所以不太需要这个东西。
c++没有, 于是就把管理生命周期的责任再次推给程序员了

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
9 [报告]
发表于 2015-11-19 22:14 |显示全部楼层
回复 14# windoze

经常随手一个using namespace std; 。。。 要被tjjtds了

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
10 [报告]
发表于 2015-11-20 19:24 |显示全部楼层
本帖最后由 OwnWaterloo 于 2015-11-20 19:24 编辑

回复 18# fender0107401

lua可以@这货看看 @starwing83
"object is poor man's closure"
当然也有不少人认为反过来说才成立
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP