2

I have a std::variant of different types including int32_t, int64_t, float, double, std::string and bool. When I assign a string literal (const char*, which is not present in this variant), I assumed it will be implicitly converted to std::string and it worked as I expected with MinGW (9.0.0 64-bit). But with MSVC (2019 64-bit) it implicitly converted to bool. If I explicitly converts it to std::string and then assign it to variant it works fine with both compilers.

Here's the code

#include <iostream>
#include <variant>

#if defined(__MINGW64__) || defined(__MINGW32__)
#define CMP_NAME "[ MinGW ]"
#elif defined(_MSC_VER)
#define CMP_NAME "[ MSVC ]"
#else
#define CMP_NAME "[ Others ]"
#endif

using KInt32 = int32_t;
using KInt64 = int64_t;
using KFloat = float;
using KDouble = double;
using KString = std::string;
using KBoolean = bool;

using Variant = std::variant<
    KInt32      /*0*/,
    KInt64      /*1*/,
    KFloat      /*2*/,
    KDouble     /*3*/,
    KString     /*4*/,
    KBoolean    /*5*/
>;

int main()
{
    //passing a const char* to Variant [target is to initialize as KString]
    Variant var = "ABCDE";
    std::cout << "Build With Compiler Set " CMP_NAME << std::endl;
    std::cout << "index = " << var.index() << std::endl;
    try{
        KString &str = std::get<KString>(var);
        std::cout << "\t[string = " << str << "]" << std::endl;
    }
    catch(const std::exception &e){
        std::cout << "\t[exception = " << e.what() << "]" << std::endl;
    }
    return 0;
}

Here's the output
With MinGW 9.0.0

     Build With Compiler Set [ MSVC ]
     index = 5
          [exception = bad variant access]

With MSVC 2019

Build With Compiler Set [ MinGW ]
index = 4
    [string = ABCDE]

Index 4 denotes to KString (aka std::string) and 5 denotes to KBoolean (aka bool).
So my question is why both compilers are giving different results?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Keshav Sahu
  • 98
  • 2
  • 8

2 Answers2

2

The behavior of variant changed for this exact case in C++20. See What is the best way to disable implicit conversion from pointer types to bool when constructing an std::variant? for a longer discussion.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • Thanks. I saw that post and the bug mentioned was similar to mine. Actually I have a wrapper class. But just to save some writing I made template constructors so it was accessing it directly. Now I made another constructor for const char*, it converts string literal to KString explicitly and then it is assigned to the variant. And now it's working as I want. – Keshav Sahu Jan 05 '22 at 17:22
1

Looks like bug in standard library which has been fixed.

I've wrote this test:

#include "catch2/catch_all.hpp"
#include <variant>

TEST_CASE("std::variant literal conversion") {
    std::variant<int, double, std::string, bool> var;
    CHECK(var.index() == 0);

    var = 1;
    CHECK(var.index() == 0);
    CHECK(std::holds_alternative<int>(var));

    var = 1.0;
    CHECK(var.index() == 1);
    CHECK(std::holds_alternative<double>(var));

    var = std::string{"foo"};
    CHECK(var.index() == 2);
    CHECK(std::holds_alternative<std::string>(var));

    var = true;
    CHECK(var.index() == 3);
    CHECK(std::holds_alternative<bool>(var));

    var = "foo";
    CHECK(var.index() == 2);
    CHECK(std::holds_alternative<std::string>(var));
}

And it fails on GCC 9.4 and Clang 10.0.1 and it is fine on GCC 10.1 and Clang 11.0.

Live demo

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • It really looks like a bug. I was only using GCC/MinGW so didn't get to know about this problem but yesterday with MSVC I got strange results. Now fixed it. – Keshav Sahu Jan 05 '22 at 17:26
  • 1
    Note there a 3 major implementations of standard library. `msvc` has own implementation, `gcc` and `clang` can operate on two version of it: `libstdc++` and `libc++`, so each can have different set of bugs. – Marek R Jan 05 '22 at 17:30