6

Barry gave us this gorgeous get_index for variants:

template <typename> struct tag { };

template <typename T, typename V>
struct get_index;

template <typename T, typename... Ts> 
struct get_index<T, std::variant<Ts...>>
    : std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };

To be used as follows:

using V = variant<A, B, C>;
constexpr const size_t N = get_index<B, V>::value;  // 1

It works great in Clang (OSX).

But in Visual Studio 2017 I'm getting the following:

<source>(10): error C2039: 'index': is not a member of 'std::variant<tag<Ts>...>'
<source>(10): note: see declaration of 'std::variant<tag<Ts>...>'
<source>(11): note: see reference to class template instantiation 'get_index<T,std::variant<_Types...>>' being compiled
Compiler returned: 2

I can't see why. Any ideas?

(Full disclosure: in my project I'm actually using mpark::variant because I have been using Xcode 9, which didn't have std::variant. However, you can see from the Godbolt MCVE above that this affects the implementation with std::variant as well. I'm convinced the problem is either in the code above, or in the compiler.)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Code is fine. Compiler bug. – T.C. Dec 06 '18 at 12:44
  • 1
    Except that `get_index::value` is 1. – T.C. Dec 06 '18 at 12:45
  • 2
    One workaround is to ship the expression into a variable template. – T.C. Dec 06 '18 at 12:46
  • 2
    @Barry That's a different error because `{}}` contains an unmatched `}` – Oktalist Dec 06 '18 at 12:57
  • 1
    I bet my 2 cents it's a bug. Anyway, I see that changing in `std::variant...>{tag{}}.index()` (braces instead round parentheses) we get a different error: "(12): error C2440: 'initializing': cannot convert from 'initializer list' to 'std::variant...>' (12): note: The target type has no constructors". Using the same contruct in other places (in `main()`, by example) gives no error. – max66 Dec 06 '18 at 14:08
  • 2
    @Oktalist Oooooooooooooooooooooooooooooops – Barry Dec 06 '18 at 14:34
  • @max66 Fair enough - good enough for an answer for me that – Lightness Races in Orbit Dec 06 '18 at 14:36
  • 4
    FWIW, [it works](https://godbolt.org/z/WLJSyC) if the expression is moved into a `constexpr` function of type `std::size_t()`, but not for a `constexpr auto` function (compiler nonsensically claims that the `auto` function is used before it is defined). – Arne Vogel Dec 06 '18 at 15:44
  • @ArneVogel Nice! I wonder whether that also might make a nice answer for posterity... – Lightness Races in Orbit Dec 06 '18 at 16:15
  • @ArneVogel - you should expand your comment in an answer, IMHO. – max66 Dec 06 '18 at 16:16
  • try to use `/Permissive-` flag since it changes the code path taken inside the compiler https://devblogs.microsoft.com/cppblog/two-phase-name-lookup-support-comes-to-msvc/ – v.oddou Mar 22 '19 at 09:12

1 Answers1

2

I bet my 2 cents that is a compiler bug.

I see that if I write in main()

std::cout << std::variant<tag<int>, tag<float>>{tag<float>{}}.index() << std::endl;

the compiler doesn't complain.

And doesn't complain also if I write a template function as follows

template <typename T, typename ... Ts>
void foo ()
 { std::cout << std::variant<tag<Ts>...>(tag<T>{}).index() << std::endl; }

and I call it, from main(), with

foo<int, long, int, long long>();

No problem also declaring the following variable in main()

std::integral_constant<std::size_t, std::variant<tag<int>, tag<float>>(tag<float>{}).index()>  ic;

But if I change the get_index specialization as follows (using braces for initialization instead of round parentheses)

template <typename T, typename... Ts> 
struct get_index<T, std::variant<Ts...>>
    : std::integral_constant<std::size_t, std::variant<tag<Ts>...>{tag<T>()}.index()>
 { };

the compiler complain but with a different error

example.cpp

(12): error C2440: 'initializing': cannot convert from 'initializer list' to 'std::variant...>'

(12): note: The target type has no constructors

(13): note: see reference to class template instantiation 'get_index>' being compiled

Compiler returned: 2

Seems that, for reasons I can't understand, the compiler doesn't see std::variant<tag<Ts>...>, inside get_index, as a std::variant with all it's methods.

max66
  • 65,235
  • 10
  • 71
  • 111