#c #class #c 14 #optional-parameters
#c #класс #c 14 #необязательно-параметры
Вопрос:
Привет, я новичок в C , и я пытаюсь создать иерархию классов, в которой каждый класс представляет узел в документе схемы, например, json-schema. Взгляните на представление, например, строки. A string
может иметь три необязательных ограничения
min_length
max_length
pattern
Кроме того, a string
— это a type
, поэтому имело бы смысл иметь базовый класс, представляющий a type
, от которого наследуются все типы ( boolean
, number
, ...
) . Теперь один из способов добиться этого — написать что-то вроде
struct Type {
const std::string m_name;
Type(const std::stringamp; name)
: m_name{name}
{}
virtual X Serialize() const {
//...
}
};
struct String : Type {
const int m_max_len;
const int m_min_len;
const std::string m_pattern;
String(int min_len, int max_len, const std::stringamp; pattern)
: Type("string")
, m_min_len(min_len)
, m_max_len(max_len)
, m_pattern(pattern)
{}
X Serialize() const override {
// If min_length was not set then it should be omitted from the serialized output.
}
};
Эта String
реализация не сделает ограничения необязательными. Что делать?
Опции:
- Можно было бы использовать стратегию, в которой параметры конструктора по умолчанию устанавливаются в некоторое «недопустимое» значение, например
INT_MIN
(что сработало бы в этом случае, потому что длина не может быть отрицательной), но это не сработало бы в общем случае. Вполне может быть, что все возможные целые числа являются допустимыми значениями, то же самое относится и кpattern
параметру. - Вы не хотите иметь разные конструкторы для каждой возможной перестановки необязательных параметров. В этом случае есть три необязательных значения, которые будут давать
2^3
разные конструкторы. Кроме того, компилятор не сможет различать конструкторыString(int min_length)
иString(int max_length)
. - Можно было бы сделать что-то вроде
String(int* min_length = nullptr, int* max_length = nullptr, const std::string* nullptr)
но тогда вам пришлось бы использовать
new
/delete
или указывать lvalues для заданных параметров. - Наконец, каждый член может быть
std::unique_ptr
.String(std::unique_ptr<int> min_value nullptr, std::unique_ptr<int> max_value = nullptr, std::unique_ptr<const std::string> pattern = nullptr)
но тогда вы столкнулись бы с довольно сложными вызовами при создании экземпляра
String
. Кроме того, при реализации контейнераType
s, который может иметь собственные необязательные параметры, все быстро выходит из-под контроля.
Наконец, код должен быть совместим с C 14.
Комментарии:
1. Я не уверен, в чем на самом деле заключается ваш вопрос.
2. Я думаю, здесь будет намного проще не использовать полиморфизм
3. «поскольку длина не может быть отрицательной» — когда значение не может быть отрицательным, вы должны использовать
unsigned
целые числа, а не простыеint
.4. Я думаю, вам следует создать класс для каждого параметра, чтобы ваш вызов выглядел так
String(MinLength::default(),MaxLength(42),string)
. Также вы можете найти вдохновение в этом блоге или в этом .5. Почему это имеет
Type
отношение к вашему вопросу? Когда я пропускаю части вашего вопроса, которые относятся к вашей иерархии типов, это имеет такой же смысл, как когда я читаю все это целиком. Похоже, что этот вопрос должен быть сосредоточен на вашем классе с необязательными параметрами, за исключением полиморфизма. Одна важная деталь, которую я вижу скрытой в обсуждении полиморфизма, — это упоминание о том, что вам нужно различать «параметр не задан» и «данный параметр не накладывает никаких ограничений» (примером последнего может быть установка минимальной длины в 0). Возможно, вы захотите упомянуть эту деталь в тексте.
Ответ №1:
Вы можете просто использовать std::optional
:
String(const std::optional<int> amp;min_len, const std::optional<int> amp;max_len,
const std::optional<std::string> amp;pattern);
Type *type = new String(5, {}, std::nullptr); // last 2 parameters are omitted.
Для C 14 вы можете использовать аналогичные конструкции, которые существуют в других библиотеках с открытым исходным кодом, (например boost::optional
, folly::Optional
).
Комментарии:
1. В OP сказано «Должно быть совместимо с C 14», и
std::optional
это C 17.2. Я не думаю, что стоит передавать
std::optional
по ссылке const . Пользователь, скорее всего, не предоставит аргументы типаstd::optional
, и будут созданы временные объекты. Что фактически совпадает с передачей по значению. Однако вы, вероятно, захотите передать строковый аргумент по ссылке const (а не необязательный, построенный вокруг его копии). В этом случае вам нужно что-то какstd::optional<std::reference_wrapper<const std::string>> pattern
. Затем пользователь может передатьstd::nullopt
.
Ответ №2:
Мне не хватает очков репутации, чтобы прокомментировать @Kostas
Чтобы быть совместимым с C 14, вы можете попробовать экспериментальное пространство имен, которое имеет необязательный (если оно доступно / работает в вашем компиляторе)
#include <experimental/optional>
затем вы можете использовать
String(std::experimental::optional<int> amp;min_len,.....)
min_len.value_or(-1)
Комментарии:
1. Мне нравится эта идея (проголосовать), но, похоже, она недоступна в Visual Studio 2019 с C 2014:-(Я надеюсь, что это поможет другим, хотя
Ответ №3:
Вы можете написать свой собственный класс, который может содержать значение или нет, если вы не можете использовать std::optional . Это не похоже на много кода. Может сделать свой интерфейс таким, как у std::optioal, или может сделать что-то другое, важны данные:
class OptInt {
bool set_;
int value_;
public:
OptInt() : set_(false) , value_(0) {}
OptInt(int v) : set_(true), value_(v) {}
// can add other ways how to create it
bool isSet() const {return set_;}
int get() const {if (!set_) throw 666; return value_;}
// can add other operations you want to it.
};
Затем вы можете использовать такой созданный по умолчанию OptInt в качестве аргумента по умолчанию, и поэтому он не будет установлен, но если вызывающий объект предоставит аргумент int, он будет установлен.
Комментарии:
1. Минимальная версия создает значение, даже если оно не установлено / не предоставлено, улучшенная версия (st std one) этого не делает.
2. @Jarod42 людям, которые говорят: «Я новичок в C «, возможно, не следует связываться с новостями о размещении, которые необходимы для получения крошечной производительности. Это может быть добавлено позже, если в этом возникнет необходимость.
3. Я не просил предоставить лучшую (и более сложную) реализацию, просто сообщите, что ее можно улучшить, если это необходимо. (Например, OP может иметь типы, не являющиеся конструктивными по умолчанию).
4. Это ссылается на ответ, данный @generic_opto_guy в исходном сообщении. Я думаю, что я собираюсь попробовать этот подход. Спасибо!
Ответ №4:
Вы пытались использовать std::optional (начиная с C 17)? Я знаю, что вы упомянули о необходимости использования кода, совместимого с C 14, но есть boost::optional .