C++别名的模板参数包扩展
C++ template parameter pack expansion of alias
因此,我在这里的动机是确定多个类中的相同命名类型声明是否是相同的类型。 在此示例中,我希望看到所有 Foo、Bar 和 Baz 都有一个内部类型 Q。
#include <type_traits>
template <typename N,typename ...Ns>
using equal_type_t = typename std::enable_if_t<(std::is_same_v<N, Ns> && ...), N>;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
int main()
{
struct Qness{};
struct Foo{using Q = Qness;};
struct Bar{using Q = Qness;};
struct Baz{using Q = Qness;};
using F = EqualQ_t<Foo,Bar,Baz>;
static_assert(std::is_same_v<F,Qness>);
return 0;
}
在 clang9 中测试(赞美上帝螺栓(。
报告的错误是:
#1 with x86-64 clang 9.0.0
<source>:10:31: error: pack expansion used as argument for non-pack parameter of alias template
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
我可能可以通过做一些模板递归来解决这个问题,但我正在尝试学习尽可能使用参数包扩展。
这可能吗? 这不是允许的上下文吗? 如果我分离出几个单独的 N 类型,它可以正常工作:
template <typename N1,typename N2, typename N3, typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<N1>,ExtractQ_t<N2>,ExtractQ_t<N3>>;
我必须有一个咖啡前的脑雾,看不到我可能在哪里隐藏语法。
是否有可行的扩展变体?
错误诊断试图说equal_type_t
的第一个参数不能是一个包,但你正在将一个包扩展到其中。因此,简单的解决方法是执行您之前所做的相同操作:
template <typename N, typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<N>, ExtractQ_t<Ns>...>;
https://godbolt.org/z/j6_HGU
解包到非包 + 包中需要模板参数推断,但别名模板不会发生这种情况,请参阅 cpp首选项。您需要结构模板专用化(或模板函数调用(才能获得扣除。
不过,在这种情况下,使用 SFINAE 似乎有点奇怪。如果不满足条件,你会得到一些编译器关于SFINAE的胡言乱语。还有其他方法会导致编译过程中出现硬错误。
我想说的是,以下是编写相同代码的惯用方法,当出现问题时,它会给你一个很好的错误,并且会(不完全巧合(避免你原来的问题:
template <typename ...Ns>
struct equal_type;
template <typename N,typename ...Ns>
struct equal_type<N, Ns...>
{
static_assert((std::is_same_v<N, Ns> && ...), "These types must be the same!");
using type = N;
};
template <typename ...Ns>
using equal_type_t = typename equal_type<Ns...>::type;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
https://godbolt.org/z/u52mUE
为了完整起见,C++17 之前的方式(在折叠表达式存在之前(确实使用了递归:
template <typename N1, typename N2, typename ...Ns>
struct equal_type
{
static_assert(std::is_same_v<N1, N2>, "These types must be the same!");
using type = typename equal_type<N1, Ns...>::type;
};
template <typename N1, typename N2>
struct equal_type<N1, N2>
{
static_assert(std::is_same_v<N1, N2>, "These types must be the same!");
using type = N1;
};
https://godbolt.org/z/NKmMZD
认为这最适合自我回答,但这主要是针对马克斯的,他提出了一个返回问题,这个回答"太长了,不适合空白"。
如果我从未尝试过参数包,我可能不会在模板递归上使用这种变体来解决我的问题,但如果我没有接受过真正的问题教育,这可能是我可能会去的地方。
#include <type_traits>
template <typename N,typename ...Ns>
using equal_type_t = typename std::enable_if_t<(std::is_same_v<N, Ns> && ...), N>;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename N,typename ...Ns>
class EqualQ
{
public:
using type = equal_type_t<ExtractQ_t<N>,typename EqualQ<Ns...>::type>;
};
template <typename N>
class EqualQ<N>
{
public:
using type = ExtractQ_t<N>;
};
template <typename ...Ns>
using EqualQ_t = typename EqualQ<Ns...>::type;
int main()
{
struct Qness{};
struct Foo{using Q = Qness;};
struct Bar{using Q = Qness;};
struct Baz{using Q = Qness;};
using F = EqualQ_t<Foo,Bar,Baz>;
static_assert(std::is_same_v<F,Qness>);
return 0;
}
是的,我意识到这不是惯用语,绝对不如Max的任何解决方案干净,并且没有回答我最初的问题,而是探讨了我试图避免的内容。
不过,我确实发现以这种方式这样做的一件事是,别名不能像类模板那样专门化。 所以教育点也在那里。 我不得不将 EqualQ 转换为模板化结构。
问题是,这样做不会教育我为什么不能按照我最初想要的方式解压缩我的参数包,当然也不会了解我现在将采用的 Max 惯用模式。 :)
- 嵌套参数包扩展失败
- [temp.variadic]中关于包扩展实例化的措辞
- 模板参数部分中有关包扩展的一些混淆
- 可变参数函数参数包扩展
- c++非类型参数包扩展
- C++别名的模板参数包扩展
- 如何将参数包扩展为向量<any>
- 模板包扩展以将函数应用于连续的参数对
- C++参数包扩展
- 如何为原生UI工具包扩展Ranorex?
- 折叠表达式、参数包扩展、类成员函数中的递归
- "如果 constexpr",在 lambda 内部,在包扩展内部 - 编译器错误?
- 折叠表达式扩展包中的最大元素数
- GCC vs CLANG:将捕获的参数包扩展两次
- 未使用模板类型定义的同时参数包扩展错误
- 构造仪的类模板参数包扩展
- 使用参数包扩展生成constexpr
- 如何"duplicate"模板参数包扩展?
- 模板.参数包扩展 - 重映射类型
- 哪个编译器(如果有的话)在参数包扩展中存在错误