3
votes

I need a template to find out the order of types in which the class inherits from its bases and their index. The code works fine with clang and gcc but in Visual Studio, which is the target environment, I'm getting an internal compiler error "fatal error C1001: An internal error has occurred in the compiler.". I'm looking for some workaround or maybe an error in my code. Yes, I have already tried google.

Thanks, in advance.

#include <type_traits>
#include <iostream>

struct BaseA
{
};

struct BaseB
{
};

struct BaseC
{
};

template <class... Types>
class type_list {};

template<typename Type, typename TypeList>
struct get_idx_for_type;

template<typename Type, template<typename...> typename TypeList, typename ...Types>
struct get_idx_for_type<Type, TypeList<Types...>>
{
    template<int I, typename T, typename ...Rest>
    struct find_type;

    template<int I, typename T, typename U, typename ...Rest>
    struct find_type< I, T, U, Rest... >
    {
        // problematic line for compiler, problem is somewhere in find_type recursion
        static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, Rest...>::value;
    };

    template<int I, typename T, typename U>
    struct find_type< I, T, U >
    {
        static constexpr int value = std::is_same<T, U>::value ? I : -1;
    };

    static constexpr int value = find_type<0, Type, Types...>::value;
};

template<typename ...Bases>
struct Foo : public Bases...
{
    using base_types_list = type_list<Bases...>;
};

int main()
{
    using T = Foo<BaseA, BaseB, BaseC>;
    Foo<BaseA, BaseB, BaseC> q;

    int a = get_idx_for_type<BaseA, T::base_types_list>::value;

    std::cout << a << std::endl;

    return 0;
}
3
If you are using the latest version of visual studio I'd suggest following the instructions in the error message to submit a bug report, they are generally fixed fairly quickly these daysAlan Birtles
I am learning at C++ primer book, I meet the same problem when I practice the variadic template in vs2017David

3 Answers

2
votes

An internal compiler error is always a bug in the compiler, whether or not there is anything wrong with your code. To work around this one, you can replace:

template<int I, typename T, typename U, typename ...Rest>
struct find_type< I, T, U, Rest... >
{
    // problematic line for compiler, problem is somewhere in find_type recursion
    static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, Rest...>::value;
};

with:

template<int I, typename T, typename U, typename V, typename ...Rest>
struct find_type< I, T, U, V, Rest... >
{
    static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, V, Rest...>::value;
};

to assist VC++ in disambiguating it from:

template<int I, typename T, typename U>
struct find_type< I, T, U >
{
    static constexpr int value = std::is_same<T, U>::value ? I : -1;
};

when ...Rest is empty.

Live demo

0
votes

I'm getting an internal compiler error "fatal error C1001: An internal error has occurred in the compiler.".

So, I suppose, your code is correct but the compiler is bugged.

I'm looking for some workaround or maybe an error in my code.

I don't have your compiler, so it's a shoot in the dark, but I propose to rewrite your find_type struct using another specialization (a specialization for U and T are the same type; a specialization for U and T are different) and inherit the value from std::integral_constant.

So I propose the following

   template <int, typename ...>
   struct find_type;

   template <int I, typename T>
   struct find_type<I, T> : public std::integral_constant<int, -1>
    { };

   template <int I, typename T, typename ... Rest>
   struct find_type<I, T, T, Rest...> : public std::integral_constant<int, I>
    { };

   template <int I, typename T, typename U, typename ... Rest>
   struct find_type<I, T, U, Rest...> : public find_type<I+1, T, Rest...>
    { };
0
votes

Another possible alternative is pass through a template function, instead of a find_type struct.

By example... if you define a static constexpr method find_type_func() as follows (C++17, but I can easily adapt it to C++14 if you want)

   template <typename T, typename ... List>
   constexpr static int find_type_func ()
    {
      int ret { -1 };
      int i   { 0 };

      ((ret = (ret == -1) && std::is_same<T, List>{} ? i : ret, ++i), ...);

      return ret;
    }

you can delete the struct find_type and initialize value as follows

static constexpr int value = find_type_func<Type, Types...>();

-- EDIT --

As asked, a C++14 version

   template <typename T, typename ... List>
   constexpr static int find_type_func ()
    {
      using unused = int[];

      int ret { -1 };
      int i   { 0 };

      (void)unused { 0, (std::is_same<T, List>::value ? ret = i : ++i)... };

      return ret;
    }