如果在可推导类型上有替换,可变参数模板类型推导会使编译器崩溃

时间:2018-10-10 17:41:36

标签: c++ c++14 language-lawyer variadic-templates template-deduction

我认为我在所有编译器中都遇到了模板类型推断错误,但是在报告之前,我想确保自己没有错过任何内容。

考虑一个例子:

#include <utility>

template <std::size_t... I, typename... T>
void foo(std::index_sequence<I...>, decltype(I)..., T...) {}

int main()
{
    foo(std::make_index_sequence<3>{}, 1, 2, 3, 4, 5);
}

decltype(I)用于缩短MWE。

  • 海湾合作委员会:error: too few arguments to function
  • 使用error: no matching function for call to 'foo'
  • 导致崩溃
  • MSVC因error C3543: 'unknown-type': does not contain a parameter pack崩溃

我不明白为什么它不能扣除T,特别是因为如果我用varargs替换参数包(除了MSVC,它又有ICE),它就可以工作。

template <std::size_t... I>
void foo(std::index_sequence<I...>, decltype(I)..., ...) {}

还有许多其他方法可以满足我的需求,但这是最短的方法,我看不出它应该失败的任何原因。

更新:用演绎法替代的已知有效示例是:

template <typename T>
struct type_identity
{ using type = T; };

template <typename T, typename... U>
void foo(T, typename type_identity<T>::type, U...) {}

int main()
{
    foo(1, 2, 3, 4, 5);
}

更新#2 原始示例的修改后的版本不会使Clang崩溃,但是关于错误的注释却很奇怪note: candidate template ignored: deduced conflicting types for parameter 'T' (<int, int, int> vs. <>)

#include <utility>

template <typename T>
struct type_identity
{ using type = T; };

template <typename...>
struct type_pack {};

template <typename... T, typename... U>
void foo(type_pack<T...>, typename type_identity<T>::type..., U...) {}

int main()
{
    foo(type_pack<int, int, int>{}, 1, 2, 3, 4, 5);
}

1 个答案:

答案 0 :(得分:5)

我认为您的代码不符合标准,但是可以争论的是,应该更改标准以使其格式正确。

标准中对模板自变量推导过程的描述如下:

  1. 将明确指定的模板参数替换为函数模板声明([temp.deduct] / 2-5)。
  2. 推断出出现在parameter-type-list中的推导上下文中的其余模板参数([temp.deduct.call])。
  3. 推导的模板参数以及具有默认模板参数的模板参数将替换为函数声明[temp.deduct.call]/10。推导过程到此结束,然后进行重载解析。

因此,过程是 substitution-deduction-substitution ;替换 deduced 自变量后,没有后续的推论。 [temp.deduct]/6为该视图提供了进一步的支持:

  

在模板自变量推导过程中的某些点,必须采用利用模板参数的函数类型,并将这些模板参数替换为相应的模板自变量。当将任何显式指定的模板参数替换为函数类型时,此操作在模板参数推论的开始时进行;当替换从默认参数推论或获得的任何模板参数时,在模板参数推论的末尾也是如此。

您的示例格式错误,因为需要先演绎然后进行替换然后演绎:除非首先{em}既推导并替代了T...,否则才能推论I...