简体   繁体   中英

Policy to produce the result type of an arithmetic operation?

Consider the following example:

#include <iostream>
#include <string>

template <class Property>
struct MyClass
{
    double _data;
};

template <class Property>
inline MyClass<Property> operator+(const MyClass<Property> lhs,
                                   const MyClass<Property> rhs)
{
    return {lhs._data + rhs._data};
}

int main()
{
   MyClass<std::string> a{1.5};
   MyClass<std::string> b{2.5};
   MyClass<std::string> c = a + b;
   std::cout<<c._data<<std::endl;
   return 0;
}

It is simple and does not exhibit any problem of design (at least I think). Now consider that I want to be able to generate an operator+ (and all arithmetic operators) for classes with different property types. But there is no standard way to decide whether MyClass<Property1> + MyClass<Property2> should be a MyClass<Property1> or a MyClass<Property2> : so the good choice should be specified by the user. The question is the following: how to redesign the class (or anything else) to let the user provide a conversion strategy ? (I mean how would it be designed in the standard library or in boost ?)

EDIT: To clarify, the result type of MyClass<Property1> + MyClass<Property2> cannot be automatically generated by the compiler. This policy should be specified by the user. But how to design that in a clean way ?

Use type traits (as Kerrek SB pointed out in his comment).

template<typename T, typename U>
struct CommonPropertyTypeTraits : std::common_type<T,U>{}; //default version works if the types have a common type 

std::common_type will only work if the type is implicitly convertable. If there is no common type which is implicitly convertable or if the user wants a different one they can specialize CommonPropertyTypeTraits:

template<>
struct CommonPropertyTypeTraits<MyType,MyOtherType> {
    using type = SomeType;
} 

The body of your function would then be:

template <class Property1, class Property2>
inline MyClass<typename CommonPropertyTypeTraits<Property1,Property2>::type> operator+(const MyClass<Property1> lhs, const MyClass<Property2> rhs)
{
    return {lhs._data + rhs._data};
}

Note that this will give a pretty ugly error if there is not implicitly convertable common type and the user did not specialize the traits template. One could make a SFINAE test to make a better error:

template <class Property1, class Property2, typename=typename CommonPropertyTypeTraits<Property1,Property2>::type>
inline MyClass<typename CommonPropertyTypeTraits<Property1,Property2>::type> operator+(const MyClass<Property1> lhs, const MyClass<Property2> rhs)
{
    return {lhs._data + rhs._data};
}

I'm still not quite certain what you ultimately want to use this for. If it is for dimensional analysis (ie tracking the types of different unit systems at compile time and making necessary conversions and issuing necessary errors) have a look at boost.Unit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM