C++ 17 可以处理嵌套的可变参数模板吗?

Can C++ 17 handle nested variadic templates?

本文关键字:参数 变参 处理 嵌套 C++      更新时间:2023-10-16

考虑下面的C++ 17代码,该代码测试一组枚举值以查看该集合中是否包含另一个枚举值:

enum Flag { val1, val2, val3, val4, val5 };
template<Flag arg> struct Value {
template<Flag... set> struct IsIn {
static constexpr bool value =
static_cast<bool>(((set == arg) || ...));
};
};

这按预期工作:

bool x = Value<val4>::IsIn<val1, val2, val5>::value;
// x == false
bool y = Value<val2>::IsIn<val3, val2>::value;
// y == true

但是,我想测试一组值是否都包含在另一组值中,如下所示:

template<Flag... args> struct Values {
template<Flag... set> struct AreIn {
static constexpr bool value =
static_cast<bool>((Value<args>::IsIn<set...>::value && ...));
};
};

以上不是在GCC 7.3或Clang 5.0上编译的;它们都给出了相当神秘的答案,对问题几乎没有洞察力。鉴于允许在模板参数列表中扩展参数包(只要模板支持扩展),我很难弄清楚为什么这不是合法C++。

您遇到了C++语法中最令人恼火的问题之一:不可推断的依赖名称。

当你做Foo<Bar>::Baz<Quux>时,由于Foo<Bar>是一个依赖名称,你必须把template关键字放在Baz之前,以防止解析器跑掉悬崖。Clang通常非常擅长用一个有用的错误明确地告诉你这一点,但在某些情况下(比如你的),它只是爆炸并说Expected )或同样无益的东西。

有关详细信息,请参阅此其他问题

因此,要修复模板,您所要做的就是在依赖模板调用中添加template关键字:

template<Flag... args>
struct Values {
template<Flag... set>
struct AreIn {
static constexpr bool value =
static_cast<bool>((Value<args>::template IsIn<set...>::value && ...));
};
};

另请注意,static_cast<bool>()是多余的。

这解决了您的问题:替换

static_cast<bool>((Value<args>::IsIn<set...>::value && ... ))

static_cast<bool>((Value<args>::template IsIn<set...>::value && ... ))

这是为什么呢?在解析AreIn声明时,编译器无法知道Value<args>可能解析为哪些可能的类型。也许,在文件的稍后时刻,您将声明不包含模板子类IsIn而是该名称字段的Values专用化。使用template关键字向编译器承诺IsIn应该是某种模板,因此以下<...>被解析为模板参数,而不是使用set...::value(全局命名空间中的变量)进行比较运算符,这也是有意义的。

当然,有人可能会问为什么编译器不等到它知道你正在使用Values<val1, val2>,为此确实有一个模板化子类AreIn<val1, val2, val5>它有一个静态成员::value。但是,提前解析并记住部分处理的语法树正是当今编译器的工作方式,并且由于上述原因,该标准使template提示成为强制性的。