C++11 пользовательские литералы для единиц физических свойств

Я пытаюсь узнать, как использовать пользовательские литералы C++11 для единиц физических свойств. Вопрос в том, как мне избежать смешения этих единиц. Так что (8.0_kg + 8.0_km)-> выдает ошибку. есть идеи, ребята? Я новичок в C++, будь добр.

class Mass{
    public:        
        //Mass(){
        //    cout << "only Mass units allowed in here" << endl;
        //}
        //~Mass();
        long double getWeight(long double a);        
        double car, house, cat;

    private:
        long double a;


    };


long double Mass::getWeight(long double w) {

    cout << "returning argument: " << w << '\n'<< endl;
    return 0;
}


long double operator"" _km(long double d) { return d * 1000.0; }
long double operator"" _m (long double d) {return d;}
long double operator"" _cm(long double d) { return d / 100.0; }

long double operator"" _tonne(long double m) { return m * 1000.0 ; }
long double operator"" _kg(long double m) { return m ; }
long double operator"" _lb(long double m) { return m * 0.453592; }

long double getDistance(long double d){
    long double starting_d = 61.0_kg;
    long double total_d = d + starting_d;
    cout << "the distance I have run is: " << total_d << endl;
    return 0;
}

int main() {


    cout <<  6.0_km << endl;
    cout <<  6.0_km + 3.0_m << endl;
    cout <<  6.0_km + 3.0_m + 15.0_cm << '\n' << endl;

    cout <<  8.0_tonne << endl;
    cout <<  8.0_km + 4.0_kg << endl;
    cout <<  8.0_km + 4.0_kg + 21.0_lb << '\n' << endl;


    long double distance = 5.45_km;
    getDistance(distance);

    Mass obj1;
    obj1.getWeight(13.96_lb);

    cout << "This is clearly wrong:  "<<  8.0_km + 4.0_kg << endl;

    obj1.getWeight(10.96_km); // so is this


}

2 ответа

Вам нужно определить свои собственные типы, так как вы не можете ограничить то, что представляет примитив.

Вы можете использовать "теговый шаблон"1, чтобы избежать повторения операторов и тому подобного и сохранить его тип в безопасности.
Это может быть расширено, так что вы получите, например, distance * distance = area или же speed * time = distance проверено компилятором.

Вот короткий пример:

template<typename Kind>
struct Value
{
    long double value;
    Value& operator+= (Value v) { value += v.value; return *this; }
};

template <typename Kind>
Value<Kind> operator+ (Value<Kind> lhs, Value<Kind> rhs) { return lhs += rhs; }

// These types don't need definitions; we only need some unique type names.
struct M;
struct D;

using Mass = Value<M>;
using Distance = Value<D>;

Mass operator"" _kg(long double d) { return { d };}
Mass operator"" _lb(long double d) { return { d * 0.453592 };}

Distance operator"" _km(long double d) { return { d * 1000 };}
Distance operator"" _mile(long double d) { return { d * 1609 };}

int main()
{
    // OK
    Distance d = 1.2_km + 0.2_mile;
    // OK
    Mass m = 2.3_kg + 1.4_lb;      
    // invalid operands to binary expression ('Distance' (aka 'Value<D>')
    // and 'Mass' (aka 'Value<M>'))
    Distance d2 = 2.4_km + 1.2_kg; // Nope
}

1) Я не думаю, что в C++ есть установленный термин, но он очень похож на то, что Haskell называет фантомными типами.

Создать классы, представляющие числовые значения различных единиц. Вот как это было сделано задолго до C++ 11.

Пользовательские литералы могут сделать инстанцирование более читабельным, потому что это помогает сохранить обычный порядок чисел и единиц

Смотрите http://en.cppreference.com/w/cpp/language/user_literal

class MassKg
{
    double value;

    // public c'tor, numeric operators, &c.
};

// ...

MassKg mass(5.0);
DistanceM distance(3.0);
auto c = mass * distance; // may yield an instance of TorqueKgM, or MomentumKgM, therefore
// explicit functions / methods are preferrable for mixed
// multiplication or division
auto mass2 = mass + MassKg(2.0); // yiels an instance of MassKg
auto invalid = mass + distance; // compile time error
Другие вопросы по тегам