Условное создание членов внутри класса

#c #class #c 17 #if-constexpr

#c #класс #c 17 #if-constexpr

Вопрос:

Возможно ли сделать что-то вроде:

 template <unsigned majorVer, unsigned minorVer>
class Object
{
public:

    if constexpr ((majorVer == 1) amp;amp; (minorVer > 10))
        bool newField;
    else
        int newInt
};
  

или

 template <unsigned majorVer, unsigned minorVer>
class Object
{
public:

    if constexpr ((majorVer == 1) amp;amp; (minorVer > 10))
        bool newField;
    // Nothing other wise
};
  

используете c 17?
Я хотел бы изменить структуру класса на основе некоторого условия, которое можно проверить во время компиляции. Есть ли какой-либо способ добиться этого?

Комментарии:

1. Специализация на шаблонах.

2. Это возможно в языке D с static if конструкцией, но в C вам нужно играть в игры с наследованием от std::condtitional_t<your condition, struct with member, empty struct> и полагаться на оптимизацию пустой базы. Хороший доклад Андрея Александреску об этом: youtube.com/watch ? v=tcyb1lpEHm0

Ответ №1:

Вы не можете использовать if constexpr для этого. Вам пришлось бы свернуть их в один элемент, используя что-то вроде std::conditional :

 std::conditional_t<(majorVer == 1) amp;amp; (minorVer > 10), bool, int> newField;
  

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

 struct A { bool newField; };
struct B { int newInt; };
  

И либо наследовать от std::conditional_t<???, A, B> , либо иметь один из них в качестве члена.


В случае, когда вам нужен либо элемент, либо ничего, другой случай просто должен быть пустым типом. В C 20 это:

 struct E { };
[[no_unique_address]] std::conditional_t<some_condition(), bool, E> newField;
  

В C 17 и более ранних версиях вы захотите наследовать от этого, чтобы гарантировать запуск оптимизации пустой базы:

 struct B { bool field; };
struct E { };

template <unsigned majorVer, unsigned minorVer>
class Object : private std::conditional_t<some_condition(), B, E>
{ ... };
  

Комментарии:

1. Спасибо Барри за ответ. Что, если я хочу создать элемент только тогда, когда выполнено условие (в противном случае никакого элемента не должно быть)? Это тоже можно сделать с помощью conditional_t?

2. @Arun Конечно, пусть альтернативой будет какой-нибудь пустой тип.

3. @Barry это создаст элемент с пустым типом, что не то же самое, что вообще никакого элемента.

4. @Barry В идеале, как уже упоминал n.m, я бы хотел, чтобы элементы вообще не создавались. Возможно ли сделать что-то подобное?

5. @arun наследуется от пустой структуры.

Ответ №2:

if-constexpr относится к потоку управления, а не к расположению памяти. Возможно, для этого хорошо подойдет отражение TS. Однако, пока это не доступно, вам понадобятся другие методы.

  constexpr bool useLegacyInt(unsigned major, unsigned minor)
 {
      return (majorVer <= 1) amp;amp; (minorVer <= 10));
 }

  template<bool>
  class ObjectBase
  {
        book newField;
   };

   template<>
  class ObjectBase<true>
  {
        int newInt;
   };

   template <unsigned majorVer, unsigned minorVer>
   class Object : public ObjectBase<useLegacyInt (majorVer, minorVer)>
   {};
  

Основываясь на этом, вы могли бы внести некоторые уточнения. Вы влияете не только на члены, но и на методы. Таким образом, установщики и получатели … могут иметь разную подпись. Защищенные вспомогательные функции могли бы предоставлять bool API для Object для разделения реализации.

Наконец, я бы не рекомендовал использовать bool, я скорее ожидаю перечисления, поскольку это может иметь несколько значений.

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

Имейте в виду, что такого рода обратная совместимость может очень быстро усложниться. Иногда лучше просто скопировать полный код в устаревшую версию и сохранить его как есть, без вмешательства нового API. Это за счет дублированного кода.