Обработка литерального нуля в единичном коде
За очень немногими исключениями (глядя на вас, шкалы температуры Фаренгейта и Цельсия) единицы являются линейными, а значение нуля одновременно является аддитивным тождеством для всех единиц сразу.
Итак, данный
auto speed = dimensioned(20, _meter/_second);
auto power = dimensioned(75, _watt);
затем
if (speed < 6) ...
if (power > 17) ...
не имеет смысла, чем
if (speed > power) ...
вам следует написать
if (speed < dimensioned(6, _mile/_hour)) ...
Однако это имеет смысл:
if (speed < 0)
потому что 0 м/с == 0 миль/ч == 0 A.U./fortnight или любые другие единицы, которые вы хотите использовать (для скорости). Тогда возникает вопрос, как включить это и только это использование.
С++ 11 явных операторов и контекстное преобразование в bool
избавились от необходимости идиомы "safe-bool". Похоже, эта проблема может быть решена с помощью сопоставимой идиомы "безопасного нуля":
struct X
{
int a;
friend bool operator<(const X& left, const X& right) { return left.a < right.a; }
private:
struct safe_zero_idiom;
public:
friend bool operator<(const X& left, safe_zero_idiom*) { return left.a < 0; }
};
К сожалению, похоже, что развернутые библиотеки размерности/единицы не делают этого. (Этот вопрос возник, потому что я действительно хотел проверить, был ли std::chrono::duration
отрицательным). Это полезно? Есть ли случаи, которые могут привести к его провалу? Есть ли более простой способ разрешить сравнение с нулем?
Предполагается, что вместо того, чтобы внедрять это для отдельных операторов, должно существовать неявное преобразование из буквенного нуля в теги, помеченные тегом.
Я отмечаю, что он позволяет
X{1} < nullptr
как допустимое выражение:( и, к сожалению, предоставление недоступной перегрузки типа std::nullptr_t
не исправляет это, поскольку стандарт говорит в разделе 4.10
Константа нулевого указателя интегрального типа может быть преобразована в prvalue типа std::nullptr_t
.
Ответы
Ответ 1
- Да. Вы очень легко убедили меня, что это полезно.
- Вы уже указали точку отказа, для
nullptr
. Я не мог думать ни о чем другом.
Мои попытки разработать механизм запрета nullptr
, но позволить 0
все приводили к сложным схемам, которые не работали. В принципе, поскольку невозможно указать С++, вам нужен параметр функции constexpr
, это сложно (я пока не могу сказать...), чтобы создать функцию, которая принимает аргумент int
, но приводит к компиляции если значение аргумента не равно 0
.
- Если вы согласны с разрешением
nullptr
, то более простой реализацией будет использование std::nullptr_t
напрямую, а не отдельный класс safe_zero_idiom
. (По общему признанию, это не так безопасно, поскольку в вашей реализации нет доступа к типу safe_zero_idiom
.)
struct X
{
int a;
friend bool operator<(const X& left, const X& right) { return left.a < right.a; }
friend bool operator<(const X& left, std::nullptr_t) { return left.a < 0; }
};
Ответ 2
Я мог только придумать очевидное решение, которое отклоняется от того, что вы хотите:
#include <stdexcept>
#include <iostream>
#include <type_traits>
using namespace std;
#include <boost/mpl/int.hpp>
using namespace boost::mpl;
struct X
{
int a;
friend bool operator<(const X& left, const X& right)
{
return left.a < right.a;
}
template< typename T >
friend bool operator<(const X& left, T zero)
{
static_assert( is_same<int_<0>, T>::value, "cannot compare against arbitrary things");
return left.a < 0;
}
};
int_<0> unitless0;
int main()
{
X x;
//if (x < 3) cout << "oopsie"; // we get a build error here as excpected.
if (x < unitless0)
cout << "ok";
return 0;
}