简体   繁体   English

C++ 模板参数包扩展别名

[英]C++ template parameter pack expansion of alias

So, my motivation here is to determine whether the same named type declaration within several classes are the same type.所以,我在这里的动机是确定几个类中相同的命名类型声明是否是相同的类型。 In this example, I'm looking to see that all of Foo, Bar, and Baz have an internal type Q.在这个例子中,我希望看到所有的 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;
}

Tested in clang9 (praise be to godbolt).在 clang9 中测试(赞美神螺栓)。

The error reported is:报告的错误是:

#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>...>;

I could probably solve this by way of doing some template recursion, but I'm trying to learn to use parameter pack expansion wherever possible.我可能可以通过做一些模板递归来解决这个问题,但我正在努力学习尽可能使用参数包扩展。

Is this possible?这可能吗? Is this not an allowed context?这不是允许的上下文吗? If I separate out a few individual N types, it works fine:如果我分离出几个单独的 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>>;

I have to be having a pre-coffee brain-fog and can't see where I might be hosing the syntax.我必须有一个咖啡前的大脑雾,看不到我可能在哪里冲洗语法。

Is there an expansion variant of this that will work?是否有这样的扩展变体可以工作?

The error diagnostic tries to say that the first parameter of equal_type_t cannot be a pack, yet you are expanding a pack into it.错误诊断试图说明equal_type_t的第一个参数不能是包,但您正在将包扩展到其中。 Thus, the simple fix is to do the same thing you did earlier:因此,简单的解决方法是执行您之前所做的相同操作:

template <typename N, typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<N>, ExtractQ_t<Ns>...>;

https://godbolt.org/z/j6_HGU https://godbolt.org/z/j6_HGU

The unpacking into a non-pack + pack would require template argument deduction, but that doesn't happen for alias templates, see cppreference .解压成非包 + 包需要模板参数推导,但别名模板不会发生这种情况,请参阅cppreference You would need a struct template specialization (or template function call) to get deduction.您需要一个结构模板专业化(或模板 function 调用)来获得扣除。


Using SFINAE seems a little weird in this case though.不过,在这种情况下使用 SFINAE 似乎有点奇怪。 If the condition is not fulfilled, you get some compiler gibberish about SFINAE thrown in your face.如果条件不满足,你会遇到一些关于 SFINAE 的编译器乱码 There are other ways to cause a hard error during compilation.还有其他方法可以在编译期间导致硬错误。

I would say the following is the idiomatic way to write the same code, which gives you a good error when there is a problem and would (not exactly coincidentally) avoid your original problem:我想说以下是编写相同代码的惯用方式,它在出现问题时会给你一个很好的错误,并且会(并非完全巧合)避免你原来的问题:

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 https://godbolt.org/z/u52mUE


For completeness, the pre-C++17 way (before fold expressions existed) does indeed use recursion:为了完整起见,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 https://godbolt.org/z/NKmMZD

Figured this was best suited to a self-answer, but this is mostly directed at Max, who asked a return question, and this response 'is too long to fit in the margins'.认为这最适合自我回答,但这主要是针对 Max,他提出了一个返回问题,而这个回答“太长,不适合空白”。

If I'd never tried parameter packs, I probably wouldn't have gotten to this variation on template recursion to solve my problem, but it's probably where I might have gone to if I hadn't been educated on the real issue.如果我从未尝试过参数包,我可能不会使用模板递归的这种变体来解决我的问题,但如果我没有接受过真正问题的教育,我可能会去那里。

#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;
}

Yes, I realize it's not idiomatic, definitely less clean than either of Max's solutions, and doesn't answer my original question, but explores what I was trying to avoid.是的,我意识到这不是惯用的,绝对不如 Max 的任何一个解决方案干净,并且没有回答我最初的问题,但探讨了我试图避免的问题。

One of the things I did discover doing it in this manner though was that aliases can't be specialized like class templates can.我确实发现以这种方式做的一件事是别名不能像 class 模板那样专门化。 So point of education there too.所以教育点也在那里。 I had to turn EqualQ into a templated struct.我不得不把 EqualQ 变成一个模板结构。

The thing is, doing it this way wouldn't have educated me on why I couldn't unpack my parameter packs the way I originally wanted to, and certainly not to Max's idiomatic pattern, which I shall now be adopting.问题是,这样做不会让我知道为什么我不能按照我最初想要的方式解压我的参数包,当然也不会按照我现在将采用的 Max 的惯用模式。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM