Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 member initialization list ambiguity

Tags:

c++

linux

c++11

I am struggling with what appears to be an ambiguity in c++11 symbol resolution due to the GNU standard library implementation in this environment:

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

Example:

#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;
}

Compiler error:

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

Command:

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

The scope resolution operator does not appear to be applicable in member initialization lists so I can't figure out how to resolve the ambiguity. This sample compiles fine without the c++11 flag.

like image 519
Chris Hutchinson Avatar asked Nov 12 '15 20:11

Chris Hutchinson


3 Answers

From my own compilation attempt, it looks like glibc is doing something stupid and #defineing common lowercase words.

When I add the following after the #includes, it compiles.

#undef major
#undef minor
like image 34
Myria Avatar answered Nov 20 '22 15:11

Myria


Another way is to use braces:

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

Then the macros will not interfere because they are function-like macros and need parentheses to be invoked.

like image 80
Johannes Schaub - litb Avatar answered Nov 20 '22 16:11

Johannes Schaub - litb


It looks like major and minor are macros defined in sys/sysmacros.h which is being brought in by <iostream>, which is problematic since these names are not reserved for the implementation. Note if the code is reduced and we solely include sys/sysmacros.h directly the same problem will occur.

When I compile this with clang on Wandbox I get this more informative error which allows us to see where the conflict is coming from:

 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)
                     ^~~~~~~~~~~~~~~~~~~

We can find this documented in the following bug report identifier major macro expanded into gnu_dev_major:

The problem is that g++ adds -D_GNU_SOURCE and major() is a macro in _GNU_SOURCE (or _BSD_SOURCE or when no feature set is requested).

You can always #undef major after including headers.

and:

makedev(), major() and minor() should be functions, not macros.

It looks they were introduced in GNUC >= 2 in sys/sysmacros.h for backward compatibility. It is not a good idea.

Fiddling with macros is dangerous since it pollutes user name space.

and so we can see undef the macros is one solution and it is unfortunately the recommended solution since this is a won't fix bug:

There will be no change. If some code does not like the macros, add #undefs after the appropriate #include. The macros are part of the API and removing them only causes problems.

We could use {} in the member intializer but this requires changes the types of the parameters since leaving them as int would be a narrowing conversion which is not allowed. Of course changing names is also a possible solution.

Update

I filed a glibc bug report. There is some questions about whether this is really a gcc or a glibc bug, the details are rather involved.

like image 15
Shafik Yaghmour Avatar answered Nov 20 '22 15:11

Shafik Yaghmour