如何在具有不同模板参数的c++模板之间隐式强制转换

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

本文关键字:之间 c++ 转换 参数      更新时间:2023-10-16

我试图创建一个c++模板类包装整数值,并检查该值始终在有效范围内,因此简化的代码如下:

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

前面的代码可以工作,但是在使用该类时它有一个小缺点。下面是一个示例:

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  

因此,要在不同范围之间赋值,我必须显式转换为给定的TYPE。我想有一个解决方案,以避免有这种显式转换和处理IntegerRange类,因为它们是正常的整数。因此,开发人员应该有一种感觉,他正在处理普通整数,而不是类。

为了解决这个问题,我尝试了不同的方法。下面的一个作为附加的构造函数:
template<typename RANGE_TYPE>  
IntegerRange(const RANGE_TYPE &value) :  
        mValue(static_cast<const TYPE>(value))  
{  
    if (MIN > mValue || mValue > MAX)  
    {  
        throw OutOfRangeError();  
    }  
}  

然而,即使这有效,我也不喜欢太多,因为RANGE_TYPE可以是任何能够转换为type的类型,并且我希望将其限制为只有IntegerRange类。为了将其仅限制为IntegerRange类,我尝试了以下操作,但它没有编译,我不明白原因:

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

问题是2:
*为什么最后一段代码不能编译,我需要修改什么来编译它。
*有没有更好的方法来避免我错过的显式转换?

谢谢

首先,你不应该使用ARG_MAX作为模板名,因为它可能已经被定义为POSIX数字常量。

其次,您应该删除IntegerRange的第三个模板参数中的typename:
IntegerRange(const IntegerRange<ARG_MIN, ARG_MAX, ARG_TYPE> &value) :

也许你应该将value转换为ARG_TYPE,这将直接调用你的operator ARG_TYPE(),然后让编译器从ARG_TYPE转换为TYPE,而不是转换为TYPE,让编译器从ARG_TYPE和对operator ARG_TYPE()的调用推断可能的转换。对于第一种解决方案,关于不可能的转换的编译错误可能更显式。

RANGE_TYPE可以是任何能够转换为type的类型,我想这样做将其限制为只使用IntegerRange类。把它限制在IntegerRange

只需交换模板参数的顺序并声明为

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

这可以工作,因为模板实参演绎不会隐式转换实参类型。例如float to int。



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

ab类型不同。记住,对于每个模板专门化,都会创建一个新的类模板。你不能给两个对象赋值,因为它们的类型看起来很相似。

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

为了做a = b,你需要提供复制构造函数,可以从另一个IntegerRange构造一个IntegerRange。(这里不需要赋值,因为有类型转换操作符。)

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