3
#include <iostream>
#include <string>
#include <variant>

int main()
{
    std::variant<std::string, bool> v{ "hasta la vista" };
    std::cout << std::boolalpha << std::holds_alternative<std::string>(v) << ' ' << std::holds_alternative<bool>(v) << std::endl;
}

GCC 12.1.1

$ g++ std_alternative.cpp 
$ ./a.out 
true false

GCC 8.5.0

$ g++ -std=c++17 std_alternative.cpp 
$ ./a.out 
false true

Why is the output different? Which is correct according to c++17? What should I do so that my code works on both versions of GCC the same way?

user7610
  • 25,267
  • 15
  • 124
  • 150
  • https://stackoverflow.com/questions/65530460/stdvariant-behaves-differently-in-msvc-and-gcc – user7610 Aug 09 '22 at 16:07
  • 5
    Looks like [LWG 3228](https://cplusplus.github.io/LWG/issue3228), a defect in C++17 fixed by [P1957R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1957r2.html). I guess GCC 8.5.0 doesn't have the fix backported. – Yksisarvinen Aug 09 '22 at 16:09
  • Yes, that was a particularly nasty language defect, leading to totally unintuitive behaviour. – Paul Sanders Aug 09 '22 at 16:35

2 Answers2

1
struct explicit_bool {
  bool b = false;
  template<class T,
   std::enable_if_t<std::is_same_v<T, bool>, bool> = true
  >
  explicit_bool( T v ):b(v){}
  explicit_bool(explicit_bool const&) noexcept=default;
  explicit_bool& operator=(explicit_bool const&)& noexcept=default;
  explicit_bool()noexcept=default;
  ~explicit_bool()noexcept=default;
  bool operator!() const { return !b; }
  explicit operator bool() const { return b; }
};

store one of these (instead of a bool).

It only accepts actual bools and other explicit_bools as arguments.

You may have to add some explicit casts to-from bool in your code after doing this. It may be of interest to you that (bool) as a cast always does the exact same thing as static_cast<bool> does, unlike many other C-style casts compared to C++ style casts; this can make it less painful. Another choice is !!, which converts most types to bool implicitly.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

To workaround the problem on GCC 8.5, I am changing the variant to be

std::variant<std::string, const char *, bool> v{ "hasta la vista" };

That gives me a const char * on both GCC 12 and GCC 8. Looks like const char * wins over bool.

In the subsequent code, I then handle both ways of providing a string. It is IMO better than leaving the variant unchanged and requiring users to pass std::string("hasta la vista"), or dealing with the ""s suffix magic. Either is easy to forget and then I am back in trouble!

Can't think of an even better way.

user7610
  • 25,267
  • 15
  • 124
  • 150
  • Be wary that raw pointers do not own their targets. A `const char*` held in your variant may dangle before you want it to. – Drew Dormann Aug 09 '22 at 17:19