aantron / better-enums

C++ compile-time enum to string, iteration, in a single header file
http://aantron.github.io/better-enums
BSD 2-Clause "Simplified" License
1.67k stars 173 forks source link

Microsoft Visual C++ Compiler error C2248 if using a Better Enum as a member in a class with a user-defined constructor #35

Closed FlorianWolters closed 7 years ago

FlorianWolters commented 7 years ago

Declaring a Better Enum as a member variable in a class without a user-defined constructor results in a C2248 compiler error (tested with both MSVC 12.0 + MSVC 14.0, using /W3).

The C2248 error does not occur if the default constructor is generated by the compiler (and no other constructors are defined).

Source code to reproduce the issue:

#include <iostream>

#include <enum.h>

namespace example {
  BETTER_ENUM(Boolean, bool, TRUE = true, FALSE = false)

  class Example {
    public:
     Example() {
       // NOOP
     }

     // 1. Workaround
//     Example() : boolean_enum_{Boolean::FALSE} {
//       // NOOP
//     }

     // Microsoft Visual C++ Compiler Error C2248.
     Boolean boolean_enum_;

     // 2. Workaround
//     Boolean boolean_enum_{Boolean::FALSE};
  };
}  // namespace example

int main() {
  example::Example example{};
  std::cout << example.boolean_enum_._to_string();
}

Compiler output (in German):

Fehler: C2248: "example::Boolean::Boolean": Kein Zugriff auf private Member, dessen Deklaration in der example::Boolean-Klasse erfolgte.

Suggestion

I think Better Enums should work the same as a built-in enum class. Due to this error I cannot use Better Enums in production code. This is a severe issue, since users have to modify client code if upgrading from built-in enumerations to Better Enums.

Workarounds

There are at least two possible workaround:

  1. Initialize the Better Enum member variable in the initializer list of the constructor (see code comment 1).
  2. Initialize the Better Enum member variable with a member initializer (see code comment 2).
FlorianWolters commented 7 years ago

This issue is a duplicate of #33. Sorry, but maybe the information provided here helps too.

FlorianWolters commented 7 years ago

This is also a problem if using Better Enums in a container, e.g. std::array<Boolean, 8>. Another example is this usage of boost::program_options:

boost::program_options::value<Boolean>(&boolean_)
      ->required()

What do I have to change to get rid of this bug? I think a workaround would be to initialize a Better Enum to the value of the first enumerator by default (or better: to no value, UB - same as with enum class), but I cannot find the construction code (too much macros and templates). :cry:

FlorianWolters commented 7 years ago

I think I got it. I modified https://github.com/aantron/better-enums/blob/master/enum.h#L1041 and changed private to public. Now I still have a problem with the boost::program_options::value<Boolean>(&boolean_) call:

D:\win-dev-env\native\boost\boost-1.63.0-x86\include\boost-1_63\boost\lexical_cast\detail\converter_lexical.hpp:243: Fehler: C2338: Target type is neither std::istream`able nor std::wistream`able

Why doesn't boost pick up the operator >> in enum.h?

Edit: I've used the latest release version and updated to HEAD now. Still the constructor visibility is bugged. But despite that it seems to work now.

FlorianWolters commented 7 years ago

Doh, I did not see http://aantron.github.io/better-enums/DesignDecisionsFAQ.html, so nevermind. Your library is awesome. For others: This link describes how-to change default behavior.