Как я могу создать новый примитивный тип, используя строгие определения типа в стиле C ++ 11?

Я пытаюсь подражать в C ++ отчетливый
тип
от Nim
язык программирования
. Следующий пример не
компилировать в Nim, потому что компилятор ловит переменные e а также d
имеющие разные типы (Error: type mismatch: got (Euros, float)) несмотря на
оба являются поплавком на двоичном уровне:

type
Euros = distinct float

when isMainModule:
var
e = Euros(12.34)
d = 23.3
echo (e + d)

Один из способов сделать это в C ++ — написать класс-оболочку для чисел с плавающей точкой. Но
это не работает с API, которые экспортируют тип, потому что размер не будет
быть таким же, как плавать. Или даже если размер класса соответствует длине хранилища
float, он никогда не будет соответствовать размеру типа char.
Это сработает, если вы также реализуете все возможные операторы для таких операций, как сложение, вычитание и т. Д., Но требует много типизации и дублирования кода.

Старые вопросы, такие как Создание нового примитива
тип

иметь в качестве принятого ответа использовать сильную typedef boost. Однако typedef
кажется, работает только для сигнатур типов функций, typedef не будет препятствовать двум
унаследованные от float типы, которые будут добавлены вместе, и их тип полностью изменен
(ну, потому что есть только иллюзия нового типа):

#include <boost/serialization/strong_typedef.hpp>
#include <stdio.h>

BOOST_STRONG_TYPEDEF(float, money);

void test(money a, float b)
{
int t = a + b;
printf("value is %d", t);
}

int main()
{
money a(5.5);
int euros(5);
// This is not caught!
int dollars = a + euros;
printf("dollars %d\n", dollars);
// But the compiler catches this misuse.
test(euros, a);
}

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

В этом же ответе упоминается C ++ 0x, чтобы получить сильные определения типов, поэтому я искал это
новая поддержка и обнаружил, что Сам Бьярне Страуструп дал стиль C ++ 11
основной доклад в
2012
.
Около минуты 21 он начинает говорить об этих новых сильных typedefs. если ты
скачать только слайды, страница 19 начинает говорить о Единицы СИ и позже
на страницах 22 и 23 указано, как это будет сделано. Тем не менее, я не смог
заставить примеры работать. Вот лоскутное одеяло, которое мне удалось придумать:

template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit Value(double d) : val(d) {} // construct a Value from a double
};

using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value< Unit<1,0,-1> >; // meters/second type
constexpr Value<Second> operator "" _s(long double d)
// a f-p literal suffixed by ‘_s’
{
return Value<Second> (d);
}
constexpr Value<Meter> operator "" _m(long double d)
// a f-p literal suffixed by ‘_m’
{
return Value<Meter> (d);
}

int main(void)
{
Speed sp1 = 100_m / 9.8_s;
return 42;
}

Я пытаюсь скомпилировать это под MacOSX с последней версией Xcode 5.1.1 с командной строкой:

$ g++ unit.cpp -std=c++11
unit.cpp:13:25: error: constexpr function's return type 'Value<Second>' is not a
literal type
constexpr Value<Second> operator "" _s(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<0, 0, 1> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:18:24: error: constexpr function's return type 'Value<Meter>' is not a
literal type
constexpr Value<Meter> operator "" _m(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<1, 0, 0> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:26:20: error: no matching literal operator for call to 'operator "" _m'
with argument of type 'unsigned long long' or 'const char *', and no
matching literal operator template
Speed sp1 = 100_m / 9.8_s;
^
unit.cpp:26:28: error: no matching literal operator for call to 'operator "" _s'
with argument of type 'long double' or 'const char *', and no matching
literal operator template
Speed sp1 = 100_m / 9.8_s;
^
4 errors generated.

Может быть, примеры приведены на слайдах, и мне не хватает еще кода? Есть ли у кого-нибудь полный пример того, что Бьярне пытался продемонстрировать?

16

Решение

В C ++ 11 нет сильных определений типов. Есть поддержка юнитов с <chrono> но это совсем другое. Никто не может согласиться с тем, какое поведение должны иметь сильные typedefs, так что никогда не было предложений для них, которые никуда не делись, поэтому они не только ни в C ++ 11, ни в C ++ 14, в этом нет реальной перспективы. время, когда они попадут в любой будущий стандарт.

15

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

Компиляторы C ++ обычно ожидают параметр командной строки -std=c++11 (или же -std=c++0x для чуть более старых, соответственно), чтобы активировать поддержку C ++ 11.

вообще не поддерживает стиль C ++ 11.

Нет, это прекрасно. Поддержка GCC 4.7.2 может быть проверил здесь. Чтобы активировать некоторые экспериментальные функции, передайте -std=gnu++11,

И Clang 3.4 на самом деле поддерживает практически все в C ++ 11 и уже многое из C ++ 1y.

5

Не уверен, что это то, что вы хотите, это некрасиво, но это работает 🙂
Вы можете обернуть тип в класс шаблона,

template <typename T, int N> // N is used for tagging
struct strong_typedef
{
using strong_type = strong_typedef<T,N>; // typedef for the strong type
using type = T; // the wrapped type
T value; // the  wrapped value

strong_typedef(T val): value(val){}; // constructor
strong_typedef(){value={};}; // default, zero-initialization

// operator overloading, basic example:
strong_type& operator+(const strong_type& rhs)
{
value+=rhs.value;
return *this;
}

// display it
friend ostream& operator<<(ostream & lhs, const strong_typedef& rhs)
{
lhs << rhs.value;
return lhs;
}
};

затем использовать его как

// these are all different types
strong_typedef<double, 0> x = 1.1;
strong_typedef<double, 1> y = 2.2;
strong_typedef<double, 2> z = 3.3;

std::cout << x + x << std::endl; // outputs 2.2, can add x and x
// cout << x + y << endl; // compile-time ERROR, different types

x, y а также z 3 разных типа сейчас, из-за разных N-s используется в шаблоне. Вы можете получить доступ к типу и значению, используя поля type а также value, лайк x::value (будет двойной 1,1). Конечно, если вы напрямую typedef struct_typedef::typeвы вернулись на круги своя, так как вы теряете strong тип. Так что в основном ваш тип должен быть strong_typedef и не strong_typedef::type,

5

Есть несколько способов решить эту проблему, но так как я искал исправление кода Бьярне на слайдах презентации, я принимаю этот ответ, который @ robson3.14 оставил в комментариях к вопросу:

#include <iostream>

template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
// construct a Value from a double
constexpr explicit Value(double d) : val(d) {}
};

using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value<Unit<1,0,-1>>; // meters/second type

// a f-p literal suffixed by ‘_s’
constexpr Value<Second> operator "" _s(long double d)
{
return Value<Second> (d);
}
// a f-p literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(long double d)
{
return Value<Meter> (d);
}
// an integral literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(unsigned long long d)
{
return Value<Meter> (d);
}

template<int m1, int k1, int s1, int m2, int k2, int s2>
Value<Unit<m1 - m2, k1 - k2, s1 - s2>> operator / (Value<Unit<m1, k1, s1>> a, Value<Unit<m2, k2, s2>> b)
{
return Value<Unit<m1 - m2, k1 - k2, s1 - s2>>(a.val / b.val);
}

int main()
{
Speed sp1 = 100_m / 9.8_s;
std::cout << sp1.val;
}
3
По вопросам рекламы [email protected]