如何使用C ++ 11样式的强typedef创建新的基本类型?

时间:2014-05-18 19:33:04

标签: c++ c++11 boost nimrod nim

我尝试使用distinct type在C ++中模拟Nim programming language。以下示例不会成功 在Nim中编译,因为编译器捕获变量ed 尽管有不同的类型(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试图展示的完整例子吗?

5 个答案:

答案 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

xyz现在有3种不同的类型,因为模板中使用了不同的N。您可以使用typevalue字段访问类型和值,例如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;