简体   繁体   中英

Using Parameter Pack Expansion to Generate a Constexpr

I have the following code:

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

I am trying to create a bitmask based on the EvenType arguments passed into the template. Unfortunately the number of EvenTypes passed is variadic. But since we have all the arguments at compile time it seems like it should be more than possible to calculate a value based on the given arguments at compile time as a constexpr . What I get on the other hand is:

expression did not evaluate to a constant

For the listeners variable. Any ideas? Thanks in advance.

PS On a completely unrelated note if anyone has any idea how I can eliminate the lengthy friend class declaration without taking a performance hit and keeping the InternalGetType() function hidden I would love and greatly appreciate hearing it.

Edit

In response to a recent suggestion I am limited to using C++14

Edit

This is what I have done to make it work using recursive template calls 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;
}

If you can use C++17, fold expressions (see HolyBlackCat's answer) is (IMHO) a way really simple and elegant to solve the problem.

If you can't use C++17... the best I can imagine is the development of a static constexpr method for class A ; something as follows

constexpr static unsigned short calcCombinedBitMask ()
 {
   using unused = unsigned short [];

   unsigned short  ret { 0 };

   (void)unused { 0, ret |= CalcBitMask(events)... };

   return ret;
 }

So you can initialize listener in this way

static constexpr unsigned short listeners = calcCombinedBitMask();

For C++11, I suggest a solution a little different from you own

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...>(); }

That asks for fold expressions . Note that they're a relatively new feature, added in C++17.

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

Also you forgot to make CalcBitMask() constexpr .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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