【问题标题】:Variadic template compilation issue on MS Visual Studio 2017MS Visual Studio 2017 上的可变参数模板编译问题
【发布时间】:2019-05-06 01:05:28
【问题描述】:

我需要一个模板来找出类从其基类及其索引继承的类型顺序。该代码适用于 clang 和 gcc,但在作为目标环境的 Visual Studio 中,我收到内部编译器错误“致命错误 C1001:编译器中发生内部错误。”。我正在寻找一些解决方法,或者我的代码中可能有错误。是的,我已经尝试过 google。

谢谢,提前。

#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;
}

【问题讨论】:

  • 如果您使用的是最新版本的 Visual Studio,我建议您按照错误消息中的说明提交错误报告,这些天它们通常会很快得到修复
  • 我在C++ primer这本书学习,我在vs2017练习variadic template时遇到了同样的问题

标签: c++ visual-studio templates variadic-templates


【解决方案1】:

另一种可能的替代方法是通过模板函数,而不是 find_type 结构。

例如...如果你定义一个static constexpr方法find_type_func()如下(C++17,但如果你愿意,我可以很容易地将它改编成C++14)

   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;
    }

你可以删除struct find_type并初始化value如下

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

-- 编辑--

按照要求,一个 C++14 版本

   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;
    }

【讨论】:

  • 非常感谢你的想法,我已经测试过了,但它只适用于clang编译器。无论如何,我也想看看c++14版本,如果你能这么好心分享一下,也许它会起作用。灵感来自@Mike Kinghan,live demo
  • @MZPRX - 只有叮当声?真的吗?也不适用于 gcc?无论如何:添加了 C++14 示例。但是在您的现场演示之后,我看到错误(如果我没记错的话)表明 vc 正在编译 C++11。例如:“'ret': 'constexpr' 函数体中不允许声明”。 constexpr 函数中的变量声明支持从 C++14 开始引入。您必须确定您使用的标准的确切版本。
【解决方案2】:

内部编译器错误始终是编译器中的错误,无论是否存在 你的代码有什么问题。要解决这个问题,您可以替换:

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, 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;
};

协助 VC++ 消除歧义:

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;
};

...Rest 为空时。

Live demo

【讨论】:

  • 非常感谢您的想法,我已经对其进行了测试,它适用于所有编译器。我会使用你的解决方案。感谢您的宝贵时间。
【解决方案3】:

我收到一个内部编译器错误“致命错误 C1001:编译器发生内部错误。”。

所以,我想,你的代码是正确的,但编译器有问题。

我正在寻找一些解决方法,或者我的代码中可能存在错误。

我没有你的编译器,所以这是在黑暗中拍摄,但我建议使用另一个专业化重写你的 find_type 结构(UT 的专业化是同一类型;专业化UT 不同)并从 std::integral_constant 继承 value

所以我提出以下建议

   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...>
    { };

【讨论】:

  • 非常感谢您的想法,我已经对其进行了测试,并得到了与原始解决方案相同的结果。 Clang OK,gcc OK,msvc 编译器错误。
猜你喜欢
  • 1970-01-01
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
相关资源
最近更新 更多