Как предотвратить повышение :: необязательно & lt; T & gt; ошибочно с 0?

boost::optional<T> (1.51) предоставляет способ создания объектов, который очень опасен для моих пользователей и который я бы хотел предотвратить. Допустим, у меня есть свой собственный целочисленный класс, и я хочу передать необязательное такое целое число и сохранить его в каком-то классе:

class myint {
public:
int m_a;
myint (int r_a) : m_a(r_a) {
}
};

struct myclass {
boost::optional<myint> content;
myclass (const boost::optional<myint>& arg) : content(arg) {
}
};

а теперь вот как пользователи будут использовать класс:

myclass(myint(13));            //correct use
myclass(boost::none);          //correct use
myclass(myint(0));             //correct use
myclass(0);                    //INCORRECT use, this easy typo
//equates boost::none which
//is not what the user meant

Я хотел бы понять, что здесь происходит, и предотвратить это поведение.


Что интересно,

myclass(1);              //does not compile

boost::none является полностью допустимым значением для моего поля, но с boost::none подкрадываться, когда пользователь пытается ввести 0 ужасно вводит в заблуждение и опасно.

Намерение может быть немного скрытым, так как я не myint класс, и у меня действительно нет class myclass это мало что дает. В любом случае мне нужно отправить около 10 дополнительных целочисленных значений в функцию, и дедупликация не будет работать. (Вы можете представить, что я спросил вас о вашем возрасте, вашем росте и вашем богатстве, и что есть три специальные кнопки, чтобы проверить, не хотите ли вы ответить на вопрос)


Я разместил ответ, который, кажется, работает ниже (построен из утки Моинга & Предложение илонесмиз, но легче). Я рад услышать комментарии об этом, хотя.

8

Решение

Не позволяйте конструктору взять boost::optional Я бы сделал что-то подобное вместо этого.

struct myclass {
boost::optional<myint> content;
myclass () = default;
explicit myclass(const myint& int_):content(int_){}
};

Однако, когда я думаю об этом, я не совсем понимаю, чего вы пытаетесь достичь и чего вы хотите избежать. Какова цель optional член?

3

Другие решения

Это страшнее, чем мне нравится, но, похоже, это решает твои проблемы. Это работает путем пересылки аргумента, данного myclass идеально подходит для пары функций, которые принимают либо int или boost::none_t, минуя неявный пользовательский конструктор. Это работает, потому что 0 Матчи int лучше чем boost::none_tи неявный пользовательский конструктор является худшим соответствием.

class myint {
public:
int m_a;
myint (int r_a) : m_a(r_a) {}
};
boost::optional<myint> myintctor(int arg) {return myint(arg);}
boost::optional<myint> myintctor(boost::none_t arg) {return arg;}

struct myclass {
boost::optional<myint> content0;
boost::optional<myint> content1;
boost::optional<myint> content2;

template<class T0, class T1, class T2>
myclass(const T0& a0, const T1& a1, const T2& a2)
:content0(myintctor(a0)), content1(myintctor(a1)), content2(myintctor(a2))
{}
};

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

3

Я думаю, что проблема имеет смысл только для дополнительного int, Одним из решений может быть предоставление двух конструкторов:

myclass() : content(boost::none) {}
myclass(myint input) : content(input) {}

Это правда, что вы теряете немного преимущество boost::optional

1

Этот код (вдохновленный Ilonesmiz), кажется, отлично справляется с работой и немного легче, чем подход от Mooing Duck, но все еще использует магический шаблонный прием.

struct myprotectedclass {
boost::optional<myint> content;

template <class T>
myprotectedclass(const T& a) :content(boost::optional<myint>(a)) {}};

Здесь доказательство.
Когда C ++ видит 0, он думает «хм, это, вероятно, int, но это может быть указатель на что-то!» (только для 0, никаких других чисел) Но если вы передадите это 0 для функции, он должен выбрать тип, и поэтому он выбирает по умолчанию int, Принимая во внимание, что в оригинале 0 был передан функции, ожидающей myint или указатель (boost::none_t это указатель). 0 это не миинт, но это может быть указатель, поэтому он выбирал тот.

1