Инициализация члена шаблона класса на основе различных условий - PullRequest
1 голос
/ 18 мая 2019

У меня есть шаблон класса, который имеет два конструктора, один принимает тип без знака, а другой принимает постоянную ссылку на себя, оба конструктора ведут себя одинаково в том, как он создает класс.

Существует 3 случая, как этот класс может быть построен, и это определяется размерами типа и типа, в который он передается. Я также хотел бы, чтобы члены инициализировались списками инициализаторов конструкторов классов.

Класс выглядит примерно так:

template<typename T>
struct Foo {
    T data;

    template<typename P>
    Foo( const P val, const unsigned char c ) :
    data { static_cast<T>( val /* some operation done to val based on each case*/ ) } {
        assert( /* based on cases above,
                and each case has its own conditions to assert */ ); 
    }

    template<typename P>
    Foo( const Foo<P>& f, const unsigned char c ) :
    data{ static_cast<T>( Foo.val /* some operation done to val based on each case*/ ) } {
        assert( /* based on cases above, 
                and each case has its own conditions to assert */ );
};

3 случая следующие:

case 1: if( sizeof(P) > sizeof(T) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly
case 2: if( sizeof(T) > sizeof(P) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly
case 3: if ( sizeof(T) == sizeof(P) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly

Есть ли простой способ сделать это? Помните, что элемент инициализируется через список инициализаторов конструкторов, но элемент данных будет инициализироваться по-разному, все в зависимости от 3 приведенных выше случаев.

Я не знаю, как я мог бы подойти к этому. Вся информация здесь должна быть доступна во время компиляции, поэтому я не понимаю, почему должны быть проблемы, но я не знаю, как настроить конструкторы, чтобы иметь эту функциональность. Кроме того, это может быть даже сделано?


EDIT

Немного предыстории для типов T & P: оба значения T & P являются следующими:

Типы: Размер в байтах: Размер в битах:

  • std::uint8_t 1 байт 8 бит
  • std::uint16_t 2 байта 16 бит
  • std::uint32_t 4 байта 32 бита
  • std::uint64_t 8 байтов 64 бита

Как только мы определили наши 3 условия, основанные на сравнении двух размеров, утверждения работают следующим образом: переданный символ без знака также является частью условий в пределах утверждений и имеет диапазон значений, которые он может быть. Вот таблица для этого: неподписанный символ, который я буду представлять с помощью idx.

using u8  = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

if ( sizeof(P) > sizeof(T) )
   // comparision formula:
   // idx <= (((sizeof(P) / sizeof(T)) - 1)
   // Foo<T = u8> From: Foo<P = u16> idx[0,1], Foo<P =u32> idx[0,3], Foo<P = u64> idx[0,7]
   // Foo<T = u16> From: Foo<P = u32> idx[0,1], Foo<P = u64> idx[0,3]
   // Foo<T = u32> From: Foo<P = u64> idx[0,1]

if ( sizeof(T) > sizeof(P) )
   // comparision formula:
   // idx <= (((sizeof(T) / sizeof(P)) - 1)
   // Foo<T = u16> From: Foo<P = u8> idx[0,1]
   // Foo<T = u32> From: Foo<P = u8> idx[0,4], Foo<P = u16> idx[0,1]
   // Foo<T = u64> From: Foo<P = u8> idx[0,7], Foo<P = u16> idx[0,3], Foo<P = u32> idx[0,1]

if ( sizeof(P) == sizeof(T) ) {
   // no conditional checks
   // no assertion
   // in this case idx is not used and is not a part of any calculation
   // the two sizes match so it's just a normal initialization.

1 Ответ

3 голосов
/ 18 мая 2019

Это ситуация, когда if constexpr не может быть использован, и вам придется прибегнуть к SFINAE. Сначала делегируйте конструкцию из конструктора Foo<P> конструктору P:

template<typename P>
Foo(const Foo<P>& f, const unsigned char c) : Foo(f.val, c) {}

Конструктор P должен иметь три варианта, один из которых включен в зависимости от размера P:

template<typename P, std::enable_if_t<(sizeof(P) > sizeof(T))>* = nullptr>
Foo(P val, const unsigned char c) : data(...) {}

template<typename P, std::enable_if_t<sizeof(P) == sizeof(T)>* = nullptr>
Foo(P val, const unsigned char c) : data(...) {}

template<typename P, std::enable_if_t<(sizeof(P) < sizeof(T))>* = nullptr>
Foo(P val, const unsigned char c) : data(...) {}

Вы также можете добавить дополнительный уровень делегирования, если хотите, чтобы конструктор P имел одинаковые утверждения независимо от размера P.

...