How can I create a new primitive type using C++11 style strong typedefs?

前端 未结 5 1459
醉梦人生
醉梦人生 2020-12-15 20:55

I\'m trying to emulate in C++ a distinct type from the Nim programming language. The following example won\'t compile in Nim because the compiler catches the variables

相关标签:
5条回答
  • 2020-12-15 21:02

    There are several ways to solve this, but since I was looking for a fix to Bjarne's code in the presentation slides, I'm accepting this answer which @robson3.14 left in comments to the question:

    #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;
    }
    
    0 讨论(0)
  • 2020-12-15 21:04

    Not sure this is what you want, it is ugly, but it works :) You can wrap the type into a template class,

    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;
        }
    };
    

    then use it as

    // 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 and z are 3 different types now, because of the different N-s used in the template. You can access the type and value using the fields type and value, like x::value (will be double 1.1). Of course if you directly typedef the struct_typedef::type, you're back to square one, as you are losing the strong type. So basically your type should be strong_typedef and not strong_typedef::type.

    0 讨论(0)
  • 2020-12-15 21:05

    There are no strong typedefs in C++11. There is support for units with <chrono> but that is a totally different thing. Nobody can agree on what behaviour strong typedefs should have, exactly, so there has never been a proposal for them that got anywhere, so not only are they in neither C++11 nor C++14, there is no realistic prospect at this time that they will get into any future Standard.

    0 讨论(0)
  • 2020-12-15 21:06

    C++ compilers generally expect the command line option -std=c++11 (or -std=c++0x for slightly older ones, respectively) to activate C++11-support.

    not supporting C++11 style at all.

    No, it perfectly does. GCC 4.7.2's support can be checked here. To activate some experimental features, pass -std=gnu++11.

    And Clang 3.4 actually supports pretty much everything in C++11 and already much out of C++1y.

    0 讨论(0)
  • 2020-12-15 21:17

    One alternative that does not involve typedef and is not strongly enforced by the compiler but makes it very hard for the programmer to make mistakes it to encode the unit in question as a struct member.

    For my arduino project I have types like

    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 };
    };
    

    and use like

    auto time_diff = millisecond<unsigned long>::zero;
    time_diff.millisecond = nowMilliseconds() - s_lastPollTime.millisecond;
    

    So with this strategy the compiler does not stop you from mixing units, but if you do then the error will always scream at you:

    total_expenses.euros = expence1.euros + expence2.dollars;
    
    0 讨论(0)
提交回复
热议问题