推导嵌套别名模板的模板参数时的编译器差异

Dav*_*man 5 c++ templates language-lawyer template-argument-deduction

考虑下面的代码片段,我们试图在该代码片段中推断出函数的template参数foobar()

struct Bar { static constexpr auto size = 5; };

template <class T, class=std::make_index_sequence<T::size>>
struct FooList;
template <class T, std::size_t... Idxs>
struct FooList<T, std::integer_sequence<std::size_t, Idxs...>> { };

struct Wrapper {
  template <class T>
  using list_type = FooList<T>;
};

template <class T>
void foobar(typename Wrapper::template list_type<T>) { }

void test() { 
  foobar(FooList<Bar>{});
}
Run Code Online (Sandbox Code Playgroud)

主要的编译器前端在是否应进行编译方面存在分歧(请参阅this godbolt)。如果我们显式地指定template参数,例如:

void test_explicit() { 
  foobar<Bar>(FooList<Bar>{});
}
Run Code Online (Sandbox Code Playgroud)

所有主要的前端都能成功地编译代码(godbolt)。有趣的是,如果我们简化推论:

struct Bar { };

struct Wrapper {
  template <class T>
  using my_type = T;
};

template <class T>
void foobar(typename Wrapper::template my_type<T>) { }

void test() { 
  foobar(Bar{});
}
Run Code Online (Sandbox Code Playgroud)

前端的另一个子集会编译而无法编译(MSVC是同时编译两者的唯一子集,请参阅此Godbolt)。

哪种实现符合此处的要求,哪些是错误的(为什么)?谁能找到C ++标准中指定其工作方式的部分?

lol*_*pop 0

正如评论中所讨论的,这__make_integer_seq. 我想提供一个解决方法:

#include <utility>

namespace workaround {
#ifdef __clang__
  template<class T, T N>
  struct make_index_sequence_helper {
      using type = __make_integer_seq<std::integer_sequence, T, N>;
  };

  template<class T, T N>
  using make_integer_sequence = typename make_index_sequence_helper<T, N>::type;

  template<std::size_t N>
  using make_index_sequence = make_integer_sequence<std::size_t, N>;
#else
  using std::make_integer_sequence;
  using std::make_index_sequence;
#endif
}
Run Code Online (Sandbox Code Playgroud)

然后再次在作品中使用它FooList

template <class T, class=workaround::make_index_sequence<T::size>>
struct FooList;
template <class T, std::size_t... Idxs>
struct FooList<T, std::integer_sequence<std::size_t, Idxs...>> { };
Run Code Online (Sandbox Code Playgroud)