【问题标题】:Using Parameter Pack Expansion to Generate a Constexpr使用参数包扩展生成 Consexpr
【发布时间】:2018-02-05 22:58:39
【问题描述】:

我有以下代码:

#include <vector>
#include <array>

using std::vector;

enum EventType {
    EventType_Collision,
    EventType_Accelerate,
    EventType_Glide
};

template<class T, EventType... events>
class A {
private:
    static unsigned short CalcBitMask(EventType e) { return 1 << e; }
    static constexpr unsigned short listeners = 0 | CalcBitMask(events)...;
protected:
    //static constexpr int InternalGetType() { return 0; }
public:
    static constexpr int GetType() { return T::InternalGetType(); }
    static constexpr int GetListeners() { return listeners; }
};

class B : public A<B, EventType_Accelerate, EventType_Collision > {
    friend class A<B, EventType_Accelerate, EventType_Collision>;
protected:
    static constexpr int InternalGetType() { return 1; }
};

我正在尝试根据传递给模板的EvenType 参数创建一个位掩码。不幸的是,传递的EvenTypes 的数量是可变的。但是由于我们在编译时拥有所有参数,因此在编译时根据给定参数计算一个值似乎应该是更可能的constexpr。另一方面,我得到的是:

expression did not evaluate to a constant

对于 listeners 变量。有任何想法吗?提前致谢。

附:如果有人知道如何消除冗长的朋友类声明而不影响性能并保持 InternalGetType() 函数隐藏,我会很高兴并非常感谢听到它。

编辑

针对最近的一个建议,我仅限于使用 C++14

编辑

这就是我使用递归模板调用使其工作所做的工作 crtp.h

#pragma once
#include <vector>
#include <array>

using std::vector;

enum EventType {
    EventType_Collision,
    EventType_Accelerate,
    EventType_Glide
};

template<class T, EventType... events>
class A {
private:
    template <EventType Last>
    static constexpr unsigned short BitCalc() {
        return 1 << Last;
    }

    template <EventType First, EventType Second, EventType ...Rest>
    static constexpr unsigned short BitCalc() {
        return BitCalc<First>() | BitCalc<Second, Rest...>();
    }
    static constexpr unsigned short listeners = BitCalc<events...>();
protected:
    //static constexpr int InternalGetType() { return 0; }
public:
    static constexpr int GetType() { return T::InternalGetType(); }
    static constexpr int GetListeners() { return listeners; }
};

class B : public A<B, EventType_Accelerate, EventType_Collision > {
    friend class A<B, EventType_Accelerate, EventType_Collision>;
protected:
    static constexpr int InternalGetType() { return 1; }
};

main.cpp

#include "ctrp.h"
#include <iostream>
#include <vector>
#include<bitset>

using std::cout;
using std::vector;
using std::getchar;
using std::endl;

int main() {
    B b;
    cout << "Bitmask: " << std::bitset<16>(b.GetListeners());
    getchar();
    return 0;
}

【问题讨论】:

    标签: c++ c++14 variadic-templates template-meta-programming constexpr


    【解决方案1】:

    如果您可以使用 C++17,折叠表达式(请参阅 HolyBlackCat 的回答)是(恕我直言)解决问题的一种非常简单和优雅的方法。

    如果你不能使用C++17……我能想象的最好的就是为class A开发一个static constexpr方法;如下

    constexpr static unsigned short calcCombinedBitMask ()
     {
       using unused = unsigned short [];
    
       unsigned short  ret { 0 };
    
       (void)unused { 0, ret |= CalcBitMask(events)... };
    
       return ret;
     }
    

    所以可以这样初始化listener

    static constexpr unsigned short listeners = calcCombinedBitMask();
    

    对于 C++11,我建议的解决方案与您自己的略有不同

    static constexpr unsigned short CalcBitMask (EventType e)
     { return 1 << e; }
    
    static constexpr unsigned short BitCalc()
     { return 0; }
    
    template <EventType First, EventType ... Rest>
    static constexpr unsigned short BitCalc()
     { return CalcBitMask(First) | BitCalc<Rest...>(); }
    

    【讨论】:

    • 甜,可以的!我发誓你可以用模板做的事情,太棒了,谢谢。只是为了确保我从性能角度正确地考虑事情,每次调用 calcBitMask() 而不是使用 listeners 变量之间没有区别,因为无论如何都会预先计算和内联函数,对吗?
    • @Ryoku - 是的,可变参数模板真的很棒。对于性能问题...我不是真正的专家,但是...考虑到listenerstatic constexpr 成员; (相当)肯定是预先计算的编译时间也是如此。 calbBitMask()calcComninedBitMask()方法。因此它们可以用于编译时(例如,将consexpr 变量初始化为listener)或运行时(例如,将新值分配给非恒定变量)。因此,如果您使用listener,您将使用编译时计算值;如果你使用calcBitMask(),你可以要求它的微积分运行时间。
    • 好的,所以我遇到了一个错误。我终于能够在我的系统中构建代码,它出现了错误'ret': declaration is not allowed in 'constexpr' function body,我想这是告诉我我不能在 constexpr 中使用变量的方式。这似乎很奇怪,因为它在其范围内。
    • @Ryoku - 抱歉:我忘记了仅从 C++14 开始 constexpr 函数可能如此复杂。你是在编译 C++11 还是 C++14?如果您需要 C++11 解决方案,我可以尝试创建 calcCombinedBitMask() 的递归版本。
    • 我正在编译 C++14,我找到了一种使用递归模板函数使其工作的方法,但老实说,我对它们仍然有点不自信,所以我会很感激(如果它不是太麻烦),如果你可以看看我刚刚添加到问题中的代码,看看a)我做对了,和/或b)有更好/更优雅的方法
    【解决方案2】:

    这要求fold expressions。请注意,它们是 C++17 中添加的一个相对较新的功能。

    static constexpr unsigned short listeners = (CalcBitMask(events) | ...);
    

    另外你忘了CalcBitMask()constexpr

    【讨论】:

    • 哇,谢谢,我不好意思说我花了多长时间才弄清楚....好吧,现在当我运行程序时,优化编译器停止工作,最终它吐出一个@ 987654325@ 用于监听器的初始化。关于它为什么会这样做的任何想法?噢,伙计@max66,你说得对。好的,这本来会很棒,而且要走的路,但不幸的是,我仅限于使用 C++14。还有其他想法吗?作为旁注,模板方式是 C++17 的一个很好的补充
    • @Ryoku 那么 max66 的回答应该会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多