Можно ли сделать что-то вроде:
template <unsigned majorVer, unsigned minorVer>
class Object
{
public:
if constexpr ((majorVer == 1) && (minorVer > 10))
bool newField;
else
int newInt
};
или
template <unsigned majorVer, unsigned minorVer>
class Object
{
public:
if constexpr ((majorVer == 1) && (minorVer > 10))
bool newField;
// Nothing other wise
};
используя С++ 17? Я хотел бы изменить структуру класса на основе некоторого условия, которое можно проверить во время компиляции. Есть ли способ добиться этого?
Это возможно в языке D с конструкцией static if
, но в C++ вам нужно играть в игры с наследованием от std::condtitional_t<your condition, struct with member, empty struct>
и полагаться на пустую базовую оптимизацию. Хороший разговор об этом от Андрея Александреску: youtube.com/watch?v=tcyb1lpEHm0
Вы не можете использовать if constexpr
для этого. Вам нужно будет свернуть их в один член, используя что-то вроде std::conditional
:
std::conditional_t<(majorVer == 1) && (minorVer > 10), bool, int> newField;
В качестве альтернативы вы можете обернуть каждый из двух типов полей в свой собственный тип:
struct A { bool newField; };
struct B { int newInt; };
И либо наследовать от std::conditional_t<???, A, B>
, либо иметь одного из них в качестве члена.
В случае, когда вам нужен либо элемент, либо ничего, в другом случае просто должен быть пустой тип. В С++ 20 это:
struct E { };
[[no_unique_address]] std::conditional_t<some_condition(), bool, E> newField;
В С++ 17 и более ранних версиях вы захотите наследовать это, чтобы убедиться, что пустая базовая оптимизация срабатывает:
struct B { bool field; };
struct E { };
template <unsigned majorVer, unsigned minorVer>
class Object : private std::conditional_t<some_condition(), B, E>
{ ... };
Спасибо Барри за ответ. Что делать, если я хочу создать член только тогда, когда условие выполнено (в противном случае член не должен быть там)? Можно ли это сделать с помощью conditional_t?
@Arun Конечно, альтернативой может быть какой-то пустой тип.
@Barry это создаст член с пустым типом, что не равнозначно отсутствию члена.
@Barry В идеале, как уже упоминал nm, я бы хотел, чтобы вообще не создавались участники. Можно ли сделать что-то подобное?
@arun наследуется от пустой структуры.
@н.м. Помидор, помидор? Это в основном то же самое.
if-constexpr относится к потоку управления, а не к структуре памяти. Возможно, отражение TS могло бы хорошо подойти для этого. Однако, пока это не доступно, вам понадобятся другие методы.
constexpr bool useLegacyInt(unsigned major, unsigned minor)
{
return (majorVer <= 1) && (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)>
{};
Исходя из этого, можно сделать некоторые уточнения. Вы влияете не только на участников, но и на методы. Так что сеттеры и геттеры... могут иметь другую подпись. Защищенные вспомогательные функции могут предоставлять логический API для Object для разделения реализации.
Наконец, я бы не рекомендовал использовать логическое значение, я скорее ожидаю перечисления, так как оно может иметь несколько значений.
Наследование от более ранней версии также возможно, если новая версия только расширяется. А с некоторыми аргументами шаблона по умолчанию вы можете делать еще больше причудливых вещей.
Имейте в виду, что такая обратная совместимость может очень быстро усложниться. Иногда лучше просто скопировать весь код в старую версию и оставить как есть, без вмешательства нового API. Это за счет дублированного кода.
Специализация шаблона.