C++11 member initialization list ambiguity
Asked Answered
N

4

36

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.

Naiad answered 12/11, 2015 at 20:51 Comment(5)
It also compiles fine with MSVC 2015Naiad
The same problem found here major and minor macros defined in sys/sysmacros.h pulled in by <iterator>Farinaceous
@Myria they are macros defined by gccGeller
great Now I have to add gnu to the list of compilers that add stupid stupid macro definitions. First being of course Visual Studio with min and max, and everything like #define CreateFile CreateFileW which is pretty much everything.Geller
Filed a glibc bug report, it is linked in my answer below.Gabrielson
B
40

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.

Beezer answered 12/11, 2015 at 21:1 Comment(5)
great answer! better solution than undefining macrosNaiad
@ChrisHutchinson "better solution than undefining" I don't know about that; They should not be there in the first place as they are not implementation reserved identifiers. I like #undefing them. (Upvoted this too, as it is a valid and clean solution for people who want to use those macros.)Pantaloons
Interesting--that's an unexpected benefit of brace-initialization.Regret
@BaummitAugen #undefing them means now you're specifically designing your code around non-standard definitions that "should not be there"; that's about as inelegant as you can get.Livia
@Leushenko it's my experience, and the stories I've heard confirm it, that if you want your code to be portable in practice, you need to work around the odd little differences in various implementations, and that's simply never going to be elegant. These #undefs should be in a header used throughout your project for portability. Note, too, that this technique allows initializing these members, but it doesn't really probably any protection against macro substitution in other syntactic contexts.Regret
M
19

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
Murrumbidgee answered 12/11, 2015 at 20:58 Comment(0)
G
15

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.

Gabrielson answered 12/11, 2015 at 20:58 Comment(4)
If it doesn't already exist I would file a new bug report against glibc. This is a result of nasty interaction of multiple header files: _GNU_SOURCE causes stdlib.h to include sys/types.h, and it causes sys/types.h to include sys/sysmacros.h. The cascading of these two behaviors is completely unintentional and unwanted.Greenwell
I concur with R..: file a new bug report (in the upstream bugzilla, not Red Hat's: sourceware.org/bugzilla) The glibc maintainers' attitudes toward this sort of thing have changed considerably since 2004.Threadgill
@R.. filed a bug report and it is linked in my answerGabrielson
I'm pleased to report that glibc has decided to remove these macros from sys/types.h in a future release. There's no exact schedule yet, though.Threadgill
E
9

/usr/include/sys/sysmacros.h has following macros defined:

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

looks like you have to undef them or use other names

Escurial answered 12/11, 2015 at 20:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.