我尝试使用distinct
type在C ++中模拟Nim
programming language。以下示例不会成功
在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不能很好地工作,因为尺寸不会很大 与浮动相同。或者即使是一个班级&#39;大小匹配a的存储长度 float,它将永远不会匹配char类型的大小。如果您还为加法,减法等操作实现所有可能的运算符,那么这将有效,但需要大量输入和重复代码。
较早的问题,例如Creating a new primitive type 已经接受使用boost的强类型的答案。但是typedef 似乎只适用于函数类型签名,typedef不会阻止两个 float-inherited类型要一起添加,它们的类型完全改变了 (好吧,因为只有新类型的幻觉):
#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带来强大的typedef,所以我找了这个 新支持并发现Bjarne Stroustrup himself gave a C++11 style keynote in 2012。 大约21分钟,他开始谈论这些新的强类型定义。如果你 仅下载幻灯片,第19页开始讨论 SI Units 以及之后的内容 第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;
}
我尝试使用最新的Xcode 5.1.1在MacOSX下使用命令行编译它:
$ 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.
也许幻灯片中给出的示例和我错过了更多代码?有人有一个Bjarne试图展示的完整例子吗?
答案 0 :(得分:15)
C ++ 11中没有强大的typedef。有<chrono>
的单位支持,但这是完全不同的事情。没有人能够就强大的typedef应该具有什么样的行为达成一致,所以从来没有一个关于它们的提议在任何地方都有,所以不仅它们既不是C ++ 11也不是C ++ 14,没有现实的前景他们将进入任何未来标准的时间。
答案 1 :(得分:5)
C ++编译器通常期望命令行选项-std=c++11
(或-std=c++0x
分别用于较旧的选项)来激活C ++ 11支持。
根本不支持C ++ 11风格。
不,它完美无缺。 GCC 4.7.2的支持可以是checked here。要激活某些实验性功能,请传递-std=gnu++11
。
而Clang 3.4实际上支持几乎所有in C++11 and already much out of C++1y。
答案 2 :(得分: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
。您可以使用type
和value
字段访问类型和值,例如x::value
(将是双1.1)。当然,如果你直接typedef
struct_typedef::type
,那么当你丢失strong
类型时,你会回到原点1。所以基本上你的类型应该是strong_typedef
而不是strong_typedef::type
。
答案 3 :(得分:4)
有几种方法可以解决这个问题,但由于我在演示幻灯片中寻找Bjarne代码的修复方法,我接受了@ 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;
}
答案 4 :(得分:0)
一种不涉及typedef且未由编译器强加强制执行的替代方法,但使程序员很难出错以将所讨论的单元编码为struct成员。
对于我的arduino项目,我有类似
的类型template <typename T>
struct millisecond {
T millisecond;
static constexpr const struct millisecond<T> zero = { 0 };
};
template <typename T>
struct microsecond {
T microsecond;
static constexpr const struct microsecond<T> zero = { 0 };
};
并使用类似
auto time_diff = millisecond<unsigned long>::zero;
time_diff.millisecond = nowMilliseconds() - s_lastPollTime.millisecond;
因此,采用这种策略,编译器不会阻止您混合单元,但如果您这样做,则错误将始终scream at you:
total_expenses.euros = expence1.euros + expence2.dollars;