v.oddou
v.oddou

Reputation: 6775

MSVC fails to recognize a constexpr function with template auto parameter

I tried to make a meta finder of index for values in value lists.

this is the code:

#include<type_traits>
#include<utility>

template <auto... Values>
struct ValueTplList;

template <auto>
constexpr int MetaFindV(int ind)
{   // not found case
    return -1;
}
template <auto NeedleV, auto V, auto... Vs>
constexpr int MetaFindV(int ind = 0)
{
    if constexpr (std::is_same_v<decltype(NeedleV), decltype(V)>)
    {
        return NeedleV == V ? ind : MetaFindV<NeedleV, Vs...>(ind + 1);
    }
    else
    {
        return MetaFindV<NeedleV, Vs...>(ind + 1);
    }
}
// main
template <auto, typename>
struct MetaIndexOfV;
// destructurer
template <auto V, template<auto...> class Tmpl, auto... Vs>
struct MetaIndexOfV<V, Tmpl<Vs...> >
    : std::integral_constant< int, MetaFindV<V, Vs...>() >
{};
// template variable version of it:
template <auto V, typename TemplInst>
constexpr int metaFindV_v = MetaIndexOfV<V, TemplInst>::value;

// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);

find the behavior here:
https://godbolt.org/z/ukwxpN

You will find that this compiles fine with gcc 7 and above, and with clang 5 and above.

I wonder if I really did something wrong by the standard that mandates such hate by MSVC. I just can't see it by myself right now :'(

It says:

(30): error C2672: 'MetaFindV': no matching overloaded function found > (31): note: see reference to class template instantiation 'MetaIndexOfV>' being compiled (30): error C2975: 'V': invalid template argument for 'MetaFindV', expected compile-time constant expression (12): note: see declaration of 'V' (30): error C2977: 'MetaFindV': too many template arguments

I think it's complaining on this line
: std::integral_constant< int, MetaFindV<V, Vs...>() >
Like if MetaFindV<V, Vs...>() had no matching function.

(end note: if you change all auto for typename, -with some little adjustments- the whole thing starts to work (but for types and typelist only of course), even though the code pattern is exactly the same.)

EDIT: according to some bot, VS team now fixed it. (I still don't find it in the release note though) https://developercommunity.visualstudio.com/t/template-constexpr-function-not-found-from-templat/490679

Upvotes: 4

Views: 562

Answers (2)

max66
max66

Reputation: 66200

Not an answer (sorry: I suspect it's a MSVC bug but I'm not sure) but a long comment.

I propose an completely different way to obtain what do you want:

#include <type_traits>
#include <string>

template <auto... Values>
struct ValueTplList
 { };

template <auto, auto>
struct strongSame : public std::false_type
 { };

template <auto A>
struct strongSame<A, A> : public std::true_type
 { };

template <auto TargetVal, auto ... Values>
constexpr int foo (ValueTplList<Values...> const &)
{
  int ind = -1;

  (void)(    (++ind, strongSame<TargetVal, Values>::value) 
          || ... || (++ind, true) );

  return std::size_t(ind) == sizeof...(Values) ? -1 : ind;
}

template <auto V, typename TemplInst>
constexpr int metaFindV_v = foo<V>(TemplInst{});

// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);

int main ()
 {
 }

Upvotes: 1

v.oddou
v.oddou

Reputation: 6775

I tried hard and failed. It seems this is a plain bug in MSVC.

My solution for the people interested, was to give up entirely :'((( Instead I went with types and value wrappers, as follow:

#include<type_traits>
#include<utility>
#include<variant>

template <typename... Types>
struct TypeList;

// auto: http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0127r1.html
template <auto... Values>
struct ValueTplList;


// meta technique to find index of a type in a typelist
template <typename>
constexpr int MetaFind(int ind)
{   // not found case
    return -1;
}
template <typename NeedleT, typename T, typename... Ts>
constexpr int MetaFind(int ind = 0)
{
    if (std::is_same_v<NeedleT, T>)
    {
        return ind;
    }
    else
    {
        return MetaFind<NeedleT, Ts...>(ind + 1);
    }
}
// flat 2 template parameters versions
template <typename T, typename T2>
struct MetaIndexOf : std::integral_constant<int, -1>
{};
template <typename T>
struct MetaIndexOf<T,T> : std::integral_constant<int, 0>
{};
// destructurer version (access the contents of a typelist)
template <typename T, template<typename...> class Tmpl, typename... Ts>
struct MetaIndexOf<T, Tmpl<Ts...> >
    : std::integral_constant< int, MetaFind<T, Ts...>() >
{};
// template variable version of it:
template <typename T, typename TemplInst>
constexpr int metaFind_v = MetaIndexOf<T, TemplInst>::value;

// test
static_assert(metaFind_v< bool, bool > == 0 );
static_assert(metaFind_v< bool, int > == -1 );
static_assert(metaFind_v< int, TypeList<bool, float, int, double> > == 2);
static_assert(metaFind_v< double, TypeList<bool, float, int, double> > == 3);
static_assert(metaFind_v< bool, TypeList<bool, float, int, double> > == 0);
static_assert(metaFind_v< long, TypeList<bool, float, int, double> > == -1);

// make a metaFind for values:

// let's try with a value wrapped in a type
template <auto V>
struct AnyConstantV : std::integral_constant<decltype(V), V>
{};

template <typename>
struct BoxAll;
// convenience helper to directly make a typelist of boxed values from values
template< template<auto...> class VL, auto... Vs >
struct BoxAll< VL<Vs...> >
{
    using type = TypeList< AnyConstantV<Vs>... >;
};

static_assert( std::is_same_v< BoxAll<ValueTplList<0>>::type, TypeList<AnyConstantV<0>> > );

template <auto V, typename VL>
constexpr int metaFindV_v = metaFind_v< AnyConstantV<V>, typename BoxAll<VL>::type >;


// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
// god dammnit visual studio ! ..... :(
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);

You can play with it at:
https://godbolt.org/z/bnfZ5r

Notice that the static_assert with the 2 fails miserably and wrongly. Again, clang passes fine here. And most funny of all, I'm shiting you not, intellisense gets it right: enter image description here (MSVC builds with no assert here, where it should)

Upvotes: 0

Related Questions