c++ - 如何定义转换运算符以允许内置运算符 += 用于添加两个类

标签 c++ c++11 visual-studio-2015

我将类定义为基于 Windows PC 的嵌入式代码模拟环境中硬件寄存器的替代品。为了无缝地工作并在嵌入式固件代码看来就像它们是硬件寄存器一样,这些类必须隐式转换为/从其适当的标准固定宽度类型(uint8_tuint16_tuint32_t 及其已签名的拷贝),基于硬件寄存器的大小和类型。

这些类工作得很好(在 this SO 答案的帮助下)除了编译器无法弄清楚它需要做隐式转换来处理 += 操作当两个这样的类型在 += 操作中组合:

#include <iostream>

class REG16
{
private:
   uint16_t _i;
public:
   REG16() = default;
   constexpr REG16(const unsigned int i) : _i((uint16_t)i * 2) {}
   constexpr operator uint16_t() const { return _i / 2; }
   REG16& operator=(const unsigned int i) { _i = (uint16_t)i * 2; }
};

class REG32
{
private:
   uint32_t _i;
public:
   REG32() = default;
   constexpr REG32(const uint32_t i) : _i(i * 2) {}
   constexpr operator uint32_t() const { return _i / 2; }
   REG32& operator=(const uint32_t i) { _i = i * 2; return *this; }
};

int main()
{
   REG16 a(12);
   REG32 b(12);
   uint32_t c;
   REG32 d;

   // Works great: 'a' is implicitly converted to a uint16_t, then added to 'c':
   c = 0;
   c += a;
   std::cout << "c = 0; c += a;     result:   c = " << c << std::endl;

   // Works great: 'b' is implicitly converted to a uint32_t, then added to 'c':
   c = 0;
   c += b;
   std::cout << "c = 0; c += b;     result:   c = " << c << std::endl;

   // Works great: 'b' and 'd' are both implicitly converted to uint32_t's,
   // then added together as uint32_t's, and assigned back to 'd' with the
   // REG32::operator=() asignment operator:
   d = 0;
   d = d + b;
   std::cout << "d = 0; d += b;     result:   d = " << d << std::endl;

   // Does not work:
   // error C2676: binary '+=': 'REG32' does not define this operator or a conversion to a type acceptable to the predefined operator
   d = 0;
   d += b; // <------ line with the error
   std::cout << "d = 0; d += b;     result:   d = " << d << std::endl;
}

如上所示,我可以只做 d = d + b; 而不是 d += b; 但这些类的全部目的是操作相同的作为一个真正的硬件寄存器,它被定义为 uint8_tuint16_tuint32_t 等,用于模拟和测试环境。我不想为了能够在模拟环境中运行而对嵌入式固件施加奇怪的限制。

有什么我可以更改或添加到我的类中以使编译器能够进行适当的隐式转换以启用为标准类型内置的内置 operator+= 的使用吗?我想避免为这些类的组合定义我自己的 operator+= 方法,因为必须处理很多排列;我宁愿编译器的隐式转换为我完成工作。

最佳答案

大约 25 年前,我曾为这个问题苦苦挣扎。今晚我搜索了一个我当时写的模板来帮助处理这些事情。我会把它给你,但我很遗憾它可能丢失了。这是一个混合,有点像下面的那个。我认为它至少解决了运算符(operator)签名扩散的问题。

感谢 Ben Voigt 建议使用 CRTP。

感谢 Jens 改进了代码。

感谢 Mike Kinghan 的回归测试。

#include <cstdint>
using std::uint32_t;
using std::uint16_t;

template<typename Derived, typename Value_t>
class number {
public:

    constexpr number(Value_t& x) : i(x) {}

    template<class R>
    Derived&
        operator+= (const R& r) {
        i += static_cast<Value_t>(r); return static_cast<Derived&>(*this);
    }
    // ... and scads of other operators that I slavishly typed out.
protected:
    ~number() {} // to prevent slicing
private:
    Value_t & i;
};


class REG16: public number<REG16, uint16_t>
{
private:
    uint16_t _i;
    using Num = number<REG16, uint16_t>;
public:
    REG16() : Num(_i) {}
    constexpr REG16(const unsigned int i) : _i(i), Num(_i) {}
    constexpr operator uint16_t() const { return _i; }
    REG16& operator=(const unsigned int i) { _i = i; }
};

class REG32 : public number<REG32, uint32_t>
{
private:
    uint32_t _i;
    using Num = number<REG32, uint32_t>;
public:
    REG32() : Num(_i) {}
    constexpr REG32(const uint32_t i) : _i(i), Num(_i) {}
    constexpr operator uint32_t() const { return _i; }
    REG32& operator=(const uint32_t i) { _i = i; return *this; }
};
#include <iostream>
int main()
{
    REG16 a(12);
    REG32 b(12);
    uint32_t c;
    REG32 d;

    // Works great: 'a' is implicitly converted to a uint16_t, then added to 'c':
    c = 0;
    c += a;
    std::cout << "c = 0; c += a;     result:   c = " << c << std::endl;

    // Works great: 'b' is implicitly converted to a uint32_t, then added to 'c':
    c = 0;
    c += b;
    std::cout << "c = 0; c += b;     result:   c = " << c << std::endl;

    // Works great: 'b' and 'd' are both implicitly converted to uint32_t's,
    // then added together as uint32_t's, and assigned back to 'd' with the
    // REG32::operator=() asignment operator:
    d = 0;
    d = d + b;
    std::cout << "d = 0; d += b;     result:   d = " << d << std::endl;

    // DID NOT WORK
    // error C2676: binary '+=': 'REG32' does not define this operator or a conversion to a type acceptable to the predefined operator
    d = 0;
    d += b; // <------ line that had the error
    std::cout << "d = 0; d += b;     result:   d = " << d << std::endl;
}

phonetagger 更新:

这就是我最终用作每个 REG 类的基类的内容:

// numericCompoundAssignmentBase is a template base class for use with types that implement numeric behavior
template<typename DT, typename NT> // DerivedType, NativeType (native types: uint8_t, uint16_t, etc.)
struct numericCompoundAssignmentBase
{
   NT  operator++ (int) { DT& dt = static_cast<DT&>(*this); NT x(dt); dt = x + 1;  return x; } // postfix ++
   NT  operator-- (int) { DT& dt = static_cast<DT&>(*this); NT x(dt); dt = x - 1;  return x; } // postfix --
   DT& operator++ ()    { DT& dt = static_cast<DT&>(*this); NT x(dt); dt = x + 1;  return dt; } // prefix --
   DT& operator-- ()    { DT& dt = static_cast<DT&>(*this); NT x(dt); dt = x - 1;  return dt; } // prefix --
   DT& operator+= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt + r;  return dt; }
   DT& operator-= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt - r;  return dt; }
   DT& operator*= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt * r;  return dt; }
   DT& operator/= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt / r;  return dt; }
   DT& operator%= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt % r;  return dt; }
   DT& operator&= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt & r;  return dt; }
   DT& operator|= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt | r;  return dt; }
   DT& operator^= (const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt ^ r;  return dt; }
   DT& operator<<=(const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt << r; return dt; }
   DT& operator>>=(const NT& r) { DT& dt = static_cast<DT&>(*this);  dt = dt >> r; return dt; }
   private: numericCompoundAssignmentBase() = default; friend DT; // ensures the correct 'DT' was specified
};

关于c++ - 如何定义转换运算符以允许内置运算符 += 用于添加两个类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49091869/

相关文章:

c++ - 如何阻止客户端向服务器发送消息?

c++ - QtOpenGL 加载带有QImage纹理的obj格式

c++ - 未调用 libcURL 进度函数

c++ - OpenGL 无效的纹理或状态

c# - 文本前面的 Visual Studio 修饰会干扰选择

C++98 诉 C++11 std::set::insert 规范

具有一个或多个空格的 C++11 正则表达式

c++ - 是否可以确定类型是否为范围枚举类型?

visual-studio - 如何使用 VS2015 远程运行测试?

build - _PublishedWebsites 文件夹中缺少库