【问题标题】:How to implicitly cast between C++ templates with different template parameters如何在具有不同模板参数的 C++ 模板之间进行隐式转换
【发布时间】:2013-05-03 09:24:05
【问题描述】:

我正在尝试创建一个包含整数值的 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:
* 为什么最后一段代码没有编译,我需要更改什么来编译它。
* 有没有更好的方法来避免我缺少的显式演员?

谢谢

【问题讨论】:

  • 错误信息是什么?
  • 错误信息是:error: conversion from 'range1_t {aka IntegerRange}' to non-scalar type 'range2_t {aka IntegerRange}'在 gcc 上请求。
  • 你能逐字发布你的整个代码吗?我认为它应该有效。
  • 我在 g++ ../lib/src/IntegerRanges.hpp:141:31 上收到以下错误:错误:预期在数字常量 ../lib/src/IntegerRanges.hpp 之前有 '>' :142:84: 错误:模板参数 3 无效

标签: c++ template-meta-programming


【解决方案1】:

首先,您不应该使用 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() 的调用。使用第一种解决方案,关于不可能转换的编译错误可能会更加明确。

【讨论】:

  • 您可能对 ARG_TYPE 转换而不是 TYPE 是正确的。但我很遗憾地说它仍然没有编译,并且出现了更多错误。 ../lib/src/IntegerRanges.hpp:153:31:错误:数字常量之前的预期“>”../lib/src/IntegerRanges.hpp:154:67:错误:“ARG_TYPE”未在此范围内声明../lib/src/IntegerRanges.hpp:154:75: 错误:模板参数 3 无效
  • 你用的是什么编译器?它在 GCC 4.7 下编译得很好。
  • @Charlie 我在 Linux 上遇到了同样的错误,因为 ARG_MAX 是一个数字常量,只需重命名模板参数即可。
  • 您解决了问题,我删除了 typename 并将 ARG_TYPE 名称更改为另一个名称,现在它可以正常工作了!谢谢!
【解决方案2】:

RANGE_TYPE 可以是任何能够转换为 TYPE 的类型,我想 将此限制为仅 IntegerRange 类。仅将其限制为 整数范围

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

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

这会起作用,因为模板参数推导不会隐式转换 arg 类型。例如浮动到 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;
}

【讨论】:

  • 实际上,通过range2_t b = a;,我认为他期望编译器调用aoperator int()b的构造函数IntegerRange(const int)。但当然,C++ 编译器的设计目的不是为了推断那么远。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多