简体   繁体   中英

How to implicitly cast between C++ templates with different template parameters

I am trying to create a C++ template class wrapping an integer value and checking that this value is always inside a valid range, so the simplified code is the following one:

struct OutOfRangeError  
{ };  

template<int MIN, int MAX, typename TYPE>  
struct IntegerRange  
{  
    private:  
    TYPE mValue;  

    public:  
    IntegerRange(const TYPE value) : mValue(value)  
    {  
        if (MIN > value || value > MAX)  
        {  
            throw OutOfRangeError();  
        }  
    }  

    operator TYPE() const  
    {  
        return mValue;  
    }  
}  

The previous code works but it has a little drawback when using this class. Here is a sample:

typedef IntegerRange<0, 4, int> range1_t;  
typedef IntegerRange<0, 5, int> range2_t;  

range1_t a = 3;  
//range2_t b = a; // This does not work  
range2_t b = static_cast<int>(a); // This works OK  

So, to assign values between different ranges I have to explicitly cast to the TYPE given. I would like to have a solution to avoid having this explicit cast and dealing with IntegerRange class as they were normal integers. So the developer should have the feeling he is dealing with normal integer instead of classes.

To solve this I tried different things. One working is the following one as an additional constructor:

template<typename RANGE_TYPE>  
IntegerRange(const RANGE_TYPE &value) :  
        mValue(static_cast<const TYPE>(value))  
{  
    if (MIN > mValue || mValue > MAX)  
    {  
        throw OutOfRangeError();  
    }  
}  

However, even if this works, I do not like too much as RANGE_TYPE can be any type able to cast to TYPE, and I would like to restrict this to only IntegerRange classes. To restrict it only to IntegerRange classes I tried the following but it is not compiling and I do not understand the reason:

template<int ARG_MIN, int ARG_MAX, typename ARG_TYPE>  
IntegerRange(const IntegerRange<ARG_MIN, ARG_MAX, typename ARG_TYPE> &value) :  
        mValue(static_cast<const TYPE>(value))  
{  
    if (MIN > value || value > MAX)  
    {  
        throw OutOfRangeError();  
    }  
}  

The questions are 2:
* Why the last piece of code is not compiling and what I need to change to compile it.
* Is there any better to avoid the explicit cast I am missing?

Thanks

First, you shouldn't use ARG_MAX as a template name since it may already be defined as a POSIX numeric constant.

Secondly, you should remove the typename in the third template argument of IntegerRange :

IntegerRange(const IntegerRange<ARG_MIN, ARG_MAX, ARG_TYPE> &value) :

Also maybe you should cast value to ARG_TYPE which will directly call your operator ARG_TYPE() and then let the compiler convert from ARG_TYPE to TYPE instead of casting to TYPE and let the compiler infer the possible conversion from ARG_TYPE and the call to operator ARG_TYPE() . With the first solution, the compilation errors regarding impossible conversions may be more explicit.

RANGE_TYPE can be any type able to cast to TYPE, and I would like to restrict this to only IntegerRange classes. To restrict it only to IntegerRange

Just interchange the order of the template arguments and declare as

template<typename TYPE, TYPE2 MIN, TYPE2 MAX>  
struct IntegerRange

This will work because template argument deduction doesn't implicitly convert arg types. for example float to int.



//range2_t b = a; // This does not work  

Of course. a and b have different types. Remember for each template specialization a new template of the class is created. You can't assign two objects because their types look similar.

class A { int a; }; class B { int a; };
A a; B b; a = b;  // Should not work

In order to do a = b you need to provide copy constructor that can construct an IntegerRange from another IntegerRange . (You don't need assignment here because you have the type conversion operator.)

template<typename TYPE2, TYPE MIN2, TYPE MAX2>
IntegerRange(const IntegerRange<TYPE2,MIN2,MAX2>& ot) {
    if (MIN > ot.mValue || ot.mValue > MAX)  
    {  
        throw OutOfRangeError();  
    }  

    mValue = ot.mValue;
}

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