Home > Net >  Conditionally initialize class variable depending on the template type
Conditionally initialize class variable depending on the template type

Time:09-30

Suppose I have the following code:

enum class Type
{
    Type32,
    Type64
};

template<Type T>
class MyClass
{
public:
    using MyType = typename std::conditional<T == Type::Type32, uint32_t, uint64_t>::type;
    
    MyType getSum()
    {
        MyType sum = 0;
        for(size_t i = 0;i < sizeof(arr);i   )
        {
            sum  = arr[i];
        }
        return sum;
    }

 private:
     //MyType arr[4] = { 0x1234, 0x5678, 0x9ABC, 0xDEF0 }; // for Type::Type32
     //MyType arr[2] = { 0x12345678, 0x9ABCDE }; // for Type::Type64
};

I try to initialize a class variable depends on the template type with the same name but different type and value. How can I do that? I probably looking for a solution that works in c 11.

CodePudding user response:

Here is a simple way:

#include <array>
#include <cstdint>
#include <type_traits>

enum class Type { Type32, Type64 };

template <Type>
struct As128Bits;

template <>
struct As128Bits<Type::Type32> {
    using Integer = std::uint32_t;

    std::array<Integer, 4> data{0x1234, 0x5678, 0x9ABC, 0xDEF0};
};

template <>
struct As128Bits<Type::Type64> {
    using Integer = std::uint64_t;

    std::array<Integer, 2> data{0x12345678, 0x9ABCDE};
};

template <Type T>
struct MyClass : private As128Bits<T> {
    using Integer = typename As128Bits<T>::Integer;
    using As128Bits<T>::data;

    Integer getSum() {
        Integer sum = 0;
        for (auto const val : data) {
            sum  = val;
        }
        return sum;
    }
};

CodePudding user response:

you can move different part into separate class and do specialization. (or you can do full class specialization if it fits)

template <Type T>
struct MyData;

template <>
struct MyData<Type::Type32>{
    uint32_t arr[4] = { 0x1234, 0x5678, 0x9ABC, 0xDEF0 };
};

template <>
struct MyData<Type::Type64>{
    uint64_t arr[2] = { 0x12345678, 0x9ABCDE };
};

template<Type T>
class MyClass: private MyData<T>{
    // common functions
};

CodePudding user response:

You can use tag dispatch (with a delegating constructor):

template<Type T>
class MyClass
{
public:
    using MyType = typename std::conditional<T == Type::Type32, uint32_t, uint64_t>::type;

    // This will call one of the constructors below
    MyClass() : MyClass(std::integral_constant<Type, T>{}) {}

    MyType getSum() { /* ... */ }

 private:

    explicit MyClass(std::integral_constant<Type, Type::Type32>) : arr{ 0x1234, 0x5678, 0x9ABC, 0xDEF0 } {}
    explicit MyClass(std::integral_constant<Type, Type::Type64>) : arr{ 0x12345678, 0x9ABCDEF0 } {}

    MyType arr[T == Type::Type32 ? 4 : 2];
};

CodePudding user response:

This is how you could do it, using a helper lambda function to determine the array type and initialize it :

#include <array>
#include <numeric>
#include <type_traits>

enum class Type
{
    Type32,
    Type64
};

// Create a lambda function template 
// if constexpr will select the right array type to return and initializes it.
template<Type T>
auto arr_initializer = []
{
    if constexpr (T == Type::Type32)
    {
        return std::array<uint32_t, 4>{0x1234, 0x5678, 0x9ABC, 0xDEF0};
    }

    if constexpr (T == Type::Type64)
    {
        return std::array<uint64_t, 2>{ 0x12345678, 0x9ABCDE };
    }
};


template<Type T>
class MyClass
{
public:
    using MyType = typename std::conditional<T == Type::Type32, uint32_t, uint64_t>::type;
    

    MyClass() :
        arr{ arr_initializer<T>() }
    {
    };


    MyType getSum()
    {
        // no raw loops (if possible)
        return std::accumulate(arr.begin(), arr.end(), 0);
    }

private:
    // arr should be of the type returned by arr_initializer lambda
    decltype(arr_initializer<T>()) arr;
};


int main()
{
    MyClass<Type::Type32> c32;
    MyClass<Type::Type64> c64;

    auto val32 = c32.getSum();
    auto val64 = c64.getSum();

    return val32;
}
  • Related