【问题标题】:C++11 member initialization list ambiguityC++11 成员初始化列表歧义
【发布时间】:2015-11-12 20:51:14
【问题描述】:

由于此环境中的 GNU 标准库实现,我正在努力解决 c++11 符号解析中似乎存在歧义的问题:

  • Arch Linux 4.2.5-1 (x86_64)
  • g++ 5.2.0
  • clang++ 3.7.0

例子:

#include <iostream>
#include <string>

struct version {

  unsigned major;
  unsigned minor;
  unsigned patch;

  version(unsigned major, unsigned minor, unsigned patch) :
    major(major), minor(minor), patch(patch) { }

  friend std::ostream & operator<<(std::ostream & out, version const& v) {
    out << v.major << ".";
    out << v.minor << ".";
    out << v.patch;
    return out;
  }

};

int main(int argc, char ** argv) {
  version v(1, 1, 0);
  std::cout << v << std::endl;
  return 0;
}

编译器错误:

error: member initializer 'gnu_dev_major' does not name a non-static data
  member or base class
error: member initializer 'gnu_dev_minor' does not name a non-static data
  member or base class

命令:

clang++ -std=c++11 -o test *.cpp

范围解析运算符似乎不适用于成员初始化列表,因此我无法弄清楚如何解决歧义。此示例在没有 c++11 标志的情况下编译良好。

【问题讨论】:

  • 它也可以在 MSVC 2015 中正常编译
  • @Myria 它们是 gcc 定义的宏
  • great 现在我必须将 gnu 添加到添加愚蠢的愚蠢宏定义的编译器列表中。首先当然是带有minmax 的Visual Studio,以及#define CreateFile CreateFileW 之类的所有内容。
  • 提交了一个 glibc 错误报告,它在我下面的答案中链接。

标签: c++ linux c++11


【解决方案1】:

另一种方法是使用大括号:

version(unsigned major, unsigned minor, unsigned patch) :
  major{major}, minor{minor}, patch{patch} { }

那么宏就不会干扰了,因为它们是类函数宏,需要括号才能调用。

【讨论】:

  • 很好的答案!比未定义宏更好的解决方案
  • @ChrisHutchinson “比取消定义更好的解决方案” 我不知道;它们首先不应该在那里,因为它们不是实现保留的标识符。我喜欢#undefing 他们。 (也赞成这一点,因为对于想要使用这些宏的人来说,它是一个有效且干净的解决方案。)
  • @BaummitAugen #undefing 他们意味着现在您正在围绕“不应该存在”的非标准定义专门设计代码;这是你能得到的最不优雅的。
  • @Leushenko 这是我的经验,我听过的故事证实了这一点,如果你希望你的代码在实践中是可移植的,你需要解决各种实现中奇怪的小差异,并且那永远不会优雅。这些#undefs 应该在整个项目中使用的标头中以实现可移植性。另请注意,这种技术允许初始化这些成员,但它实际上可能无法防止在其他句法上下文中的宏替换。
【解决方案2】:

从我自己的编译尝试来看,glibc 似乎在做一些愚蠢的事情并且#defineing 常见的小写单词。

当我在#includes 之后添加以下内容时,它会编译。

#undef major
#undef minor

【讨论】:

    【解决方案3】:

    看起来majorminor 是在sys/sysmacros.h 中定义的宏,由&lt;iostream&gt; 引入,这是有问题的,因为这些名称are not reserved for the implementation。请注意,如果代码减少并且我们只直接包含sys/sysmacros.h,则会出现同样的问题。

    当我用clang on Wandbox 编译它时,我得到了这个信息更丰富的错误,这让我们可以看到冲突来自哪里:

     usr/include/x86_64-linux-gnu/sys/sysmacros.h:67:21: note: expanded from macro 'major'
     # define major(dev) gnu_dev_major (dev)
                         ^~~~~~~~~~~~~~~~~~~
    
     usr/include/x86_64-linux-gnu/sys/sysmacros.h:68:21: note: expanded from macro 'minor'
     # define minor(dev) gnu_dev_minor (dev)
                         ^~~~~~~~~~~~~~~~~~~
    

    我们可以在以下错误报告 identifier major macro expanded into gnu_dev_major 中找到该记录:

    问题在于 g++ 添加了 -D_GNU_SOURCE 而major() 是一个宏 在 _GNU_SOURCE(或 _BSD_SOURCE 或未请求功能集时)。

    在包含标题之后,您始终可以#undef major。

    和:

    makedev()、major() 和 minor() 应该是函数,而不是宏。

    看起来它们是在 GNUC >= 2 in sys/sysmacros.h 中引入的 向后兼容性。 这不是一个好主意。

    摆弄宏是危险的,因为它会污染用户名空间。

    所以我们可以看到undef 宏是一种解决方案,不幸的是,它是推荐的解决方案,因为这是一个无法修复的错误:

    不会有任何变化。如果某些代码不喜欢宏,请添加 #undefs 在适当的#include 之后。宏是 API 和删除它们只会导致问题。

    我们可以在成员初始化程序中使用{},但这需要更改参数的类型,因为将它们保留为 int 将是不允许的缩小转换。当然改名也是一种可能的解决方案。

    更新

    我提交了glibc bug report。关于这是否真的是 gcc 或 glibc 错误存在一些疑问,细节相当复杂。

    【讨论】:

    • 如果它不存在,我会针对 glibc 提交一个新的错误报告。这是多个头文件令人讨厌的交互的结果:_GNU_SOURCE 导致stdlib.h 包含sys/types.h,它导致sys/types.h 包含sys/sysmacros.h。这两种行为的级联完全是无意和不需要的。
    • 我同意 R..:提交新的错误报告(在上游 bugzilla,而不是 Red Hat 的:sourceware.org/bugzilla)自 2004 年以来,glibc 维护者对这类事情的态度发生了很大变化。
    • @R.. 提交了错误报告,并在我的回答中链接
    • 我很高兴地报告 glibc 已决定在未来的版本中从 sys/types.h 中删除这些宏。不过,目前还没有确切的时间表。
    【解决方案4】:

    /usr/include/sys/sysmacros.h 定义了以下宏:

    # define major(dev) gnu_dev_major (dev)
    # define minor(dev) gnu_dev_minor (dev)
    # define makedev(maj, min) gnu_dev_makedev (maj, min)
    

    看起来你必须取消它们或使用其他名称

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-28
      相关资源
      最近更新 更多