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

Grz*_*icz 17 c++ boost c++11 nim-lang

我试图在C++中模拟一种与Nim编程语言不同的类型.以下示例将不在Nim中编译,因为编译器捕获变量并 具有不同的类型(),尽管两者都是二进制级别的浮点数:edError: type mismatch: got (Euros, float)

type
  Euros = distinct float

when isMainModule:
  var
    e = Euros(12.34)
    d = 23.3
  echo (e + d)
Run Code Online (Sandbox Code Playgroud)

在C++中执行此操作的一种方法是为浮点数编写包装类.但是这对于导出类型的API不适用,因为它的大小与float不同.或者即使类的大小与浮点数的存储长度匹配,它也永远不会匹配char类型的大小.如果您还为加法,减法等操作实现所有可能的运算符,那么这将起作用,但需要大量输入和复制代码.

诸如创建新原始类型之类的旧问题 具有使用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);
}
Run Code Online (Sandbox Code Playgroud)

但这几乎就是这样,test()调用不会起作用,因为签名不匹配,但语言仍允许其他操作随意修改类型.

同样的答案提到C++ 0x带来强大的typedef,所以我寻找这个新的支持,并发现Bjarne Stroustrup本人在2012年给出了C++ 11风格的主题演讲.大约21分钟,他开始谈论这些新的强类型定义.如果你只下载幻灯片,第19页开始讨论SI单位,后来第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;
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用最新的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.
Run Code Online (Sandbox Code Playgroud)

也许幻灯片中给出的例子我错过了一些代码?有人有Bjarne试图展示的完整例子吗?

Pup*_*ppy 16

C++ 11中没有强大的typedef.有单位的支持,<chrono>但这是完全不同的事情.没有人能够就强大的typedef应该具有什么样的行为达成一致,所以从来没有一个关于它们的提议在任何地方都有,所以不仅它们既不是C++ 11也不是C++ 14,没有现实的前景他们将进入任何未来标准的时间.

  • 强类型定义类似于概念和范围 - 每个人似乎都想要它们,但没有人能够就它们应该是什么达成一致. (14认同)

Grz*_*icz 7

有多种方法可以解决这个问题,但由于我正在寻找对演示幻灯片中 Bjarne 代码的修复,我接受 @robson3.14 在问题评论中留下的答案:

\n\n
#include <iostream>\n\ntemplate<int M, int K, int S> struct Unit { // a unit in the MKS system\n    enum { m=M, kg=K, s=S };\n};\n\ntemplate<typename Unit> // a magnitude with a unit\nstruct Value {\n    double val; // the magnitude\n    // construct a Value from a double\n    constexpr explicit Value(double d) : val(d) {} \n};\n\nusing Meter = Unit<1,0,0>; // unit: meter\nusing Second = Unit<0,0,1>; // unit: sec\nusing Speed = Value<Unit<1,0,-1>>; // meters/second type\n\n// a f-p literal suffixed by \xe2\x80\x98_s\xe2\x80\x99\nconstexpr Value<Second> operator "" _s(long double d)\n{\n    return Value<Second> (d);\n}\n// a f-p literal suffixed by \xe2\x80\x98_m\xe2\x80\x99\nconstexpr Value<Meter> operator "" _m(long double d)\n{\n    return Value<Meter> (d);\n}\n// an integral literal suffixed by \xe2\x80\x98_m\xe2\x80\x99\nconstexpr Value<Meter> operator "" _m(unsigned long long d)\n{\n    return Value<Meter> (d);\n}\n\ntemplate<int m1, int k1, int s1, int m2, int k2, int s2>\nValue<Unit<m1 - m2, k1 - k2, s1 - s2>> operator / (Value<Unit<m1, k1, s1>> a, Value<Unit<m2, k2, s2>> b)\n{\n    return Value<Unit<m1 - m2, k1 - k2, s1 - s2>>(a.val / b.val);\n}\n\nint main()\n{\n    Speed sp1 = 100_m / 9.8_s;\n    std::cout << sp1.val;\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Col*_*mbo 5

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.


vso*_*tco 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;
    }
};
Run Code Online (Sandbox Code Playgroud)

然后用它作为

// 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
Run Code Online (Sandbox Code Playgroud)

x,yz3种不同类型的现在,因为不同的N模板使用-s.您可以使用字段访问类型和值typevalue,像x::value(将双1.1).当然,如果你是直接typedefstruct_typedef::type,你就会回到正方形,因为你正在失去strong类型.所以基本上你的类型应该是,strong_typedef而不是strong_typedef::type.

  • @Grzegorz`x`和`y`是不同的类型,所以你不能添加它们 - 这不是"强类型定义"的重点吗?你甚至在帖子中使用了这个确切的例子`//这没有被抓住!int dollars = a + euros;` (2认同)