参数包推导的规则是什么?

4
有人能告诉我为什么这个不起作用吗?
template<char... cs> struct StaticString {};

template<char... tail, char... prefix>
constexpr bool startsWith(StaticString<prefix..., tail...>, StaticString<prefix...>)
{
    return true;
}

static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'a'>()),
              "ab starts with a");

为什么tail被推断为空?
2个回答

4

来自cpprefrence - Parameter pack

说明
...
在主类模板中,模板参数包必须是模板参数列表中的最后一个参数。
在函数模板中,模板参数包可以出现在参数列表中较早的位置,前提是所有随后的参数可以从函数参数中推导出来,或者具有默认参数。

以及cppreference - Template argument deduction

来自类型的推导
...
如果P具有包括模板参数列表<T><I>的形式之一,则该模板参数列表的每个元素Pi与其A的相应模板参数Ai匹配。如果最后的Pi是一个模板参数包扩展,那么它的模式将与A的模板参数列表中的每个剩余参数进行比较。

未被推导出的尾部参数包将被推导为空参数包。


为了使其工作,编译器必须能够推导出参数。这可以通过使用具有不同模板参数的startsWith重载来完成。您可以从最后一部分开始,其中仅第一个StaticString具有任何剩余参数。

template<char... tail>
constexpr bool startsWith(StaticString<tail...>, StaticString<>)
{
    return true;
}

你遇到了一个失败的startsWith,其中两个StaticString不同。

template<char... tail1, char... tail2>
constexpr bool startsWith(StaticString<tail1...>, StaticString<tail2...>)
{
    return false;
}

最后,当前缀被剥离后,比较其余部分的重载函数将会被执行。

template<char prefix, char... tail1, char... tail2>
constexpr bool startsWith(StaticString<prefix, tail1...>, StaticString<prefix, tail2...>)
{
    return startsWith(StaticString<tail1...>(), StaticString<tail2...>());
}

现在你可以使用各种参数来进行static_assert,例如:

static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'a'>()),
              "ab starts with a");
static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'a', 'b'>()),
              "ab starts with ab");
static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'a', 'c'>()),
              "ab does not start with ac");
static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'x', 'a'>()),
              "ab does not start with xa");

将导致 (Ubuntu 16.04, g++ 5.4) 出现以下错误:

a.cpp:23:1: 错误:静态断言失败:ab 不以 ac 开头
static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'a', 'c'>()), "ab does not start with ac"
^
a.cpp:24:1: 错误:静态断言失败:ab 不以 xa 开头
static_assert(startsWith(StaticString<'a', 'b'>(), StaticString<'x', 'a'>()), "ab does not start with xa"
^


3

这里也会发生推断 StaticString<prefix..., tail...>,由于它是贪婪的,prefix 会占用全部内容,而 tail 则为空。

接着第二个参数存在冲突的推断。


这不是因为“贪心”,而是一个未推导的上下文。8)出现在参数P内部的模板参数列表,其中包括一个不在模板参数列表的末尾的包扩展。http://en.cppreference.com/w/cpp/language/template_argument_deduction - llllllllll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接