【发布时间】:2019-07-14 07:30:19
【问题描述】:
我只是想弄清楚这段代码发生了什么以及为什么会这样:
main.cpp
#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "Register.h"
int main() {
using namespace vpc;
Reg8 r8{ 0xEF };
Reg16 expected{ 478 };
Reg16 r16a = r8 + r8;
Reg16 r16b{ r8 + r8 };
std::cout << expected << r16a << r16b;
return EXIT_SUCCESS;
}
我的 main 函数中的代码不会改变,因为这两种情况都是一样的:
这是我正在处理的operator+,这是我的第一次尝试:
template<typename Lhs, typename Rhs>
auto operator+(const Register<Lhs>& l, const Register<Rhs>& r) {
auto tmp = l.value + r.value;
if (sizeof(l.value) < sizeof(r.value))
return Register<Rhs>{ tmp };
else
return Register<Lhs>{ tmp };
}
这是程序的输出:
输出 v1
Reg16(478)
Prev: 0
hex: 0x01DE
bin: 0000000111011110
Reg16(222)
Prev: 0
hex: 0x00DE
bin: 0000000011011110
Reg16(222)
Prev: 0
hex: 0x00DE
bin: 0000000011011110
正如您在上面看到的,期望值应该是十进制的478 或十六进制的0x01DE。但是,在这种情况下,operator= 和 Register<T> 构造函数没有从 operator+ 获得适当的值。
我可以通过将我的operator+ 更改为此来解决此问题:
template<typename Lhs, typename Rhs>
auto operator+(const Register<Lhs>& l, const Register<Rhs>& r) {
return Register<decltype(l.value + r.value)>{ l.value + r.value };
}
这给了我正确的结果:
输出 v2
Reg16(478)
Prev: 0
hex: 0x01DE
bin: 0000000111011110
Reg16(478)
Prev: 0
hex: 0x01DE
bin: 0000000111011110
Reg16(478)
Prev: 0
hex: 0x01DE
bin: 0000000111011110
如果您需要查看我的完整课程实现,您可以在我的问题下方找到它 - 关注点:我正在寻找对此处行为的清晰和更好的理解。
我想知道的是为什么第一个版本没有产生正确或预期的值,以及为什么第二次尝试会产生。这两种实现之间的主要区别是什么,以及编译器中的内部情况是什么?我正在使用 Visual Studio 2017。
Register.h
#pragma once
#include <algorithm>
#include <bitset>
#include <cassert>
#include <climits>
#include <cstdint>
#include <iterator>
#include <iostream>
#include <iomanip>
#include <limits>
#include <map>
#include <string>
#include <type_traits>
namespace vpc {
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
template<typename T>
struct Register;
using Reg8 = Register<u8>;
using Reg16 = Register<u16>;
using Reg32 = Register<u32>;
using Reg64 = Register<u64>;
template<typename T>
struct Register {
T value;
T previous_value;
std::bitset<sizeof(T)* CHAR_BIT> bits;
Register() : value{ 0 }, previous_value{ 0 }, bits{ 0 } {}
template<typename U, std::enable_if_t<(sizeof(U) > sizeof(T))>* = nullptr>
explicit Register(const U val, const u8 idx = 0) :
value{ static_cast<T>((val >> std::size(bits) * idx) &
std::numeric_limits<std::make_unsigned_t<T>>::max()) },
previous_value{ 0 },
bits{ value }
{
constexpr u16 sizeT = sizeof(T);
constexpr u16 sizeU = sizeof(U);
assert((idx >= 0) && (idx <= ((sizeU / sizeT) - 1)) );
}
template<typename U, std::enable_if_t<(sizeof(U) < sizeof(T))>* = nullptr>
explicit Register(const U val, const u8 idx = 0) :
value{ static_cast<T>((static_cast<T>(val) << sizeof(U)*CHAR_BIT*idx) &
std::numeric_limits<std::make_unsigned_t<T>>::max()) },
previous_value{ 0 },
bits{ value }
{
constexpr u16 sizeT = sizeof(T);
constexpr u16 sizeU = sizeof(U);
assert((idx >= 0) && (idx <= ((sizeT / sizeU) - 1)) );
}
template<typename U, std::enable_if_t<(sizeof(U) == sizeof(T))>* = nullptr>
explicit Register(const U val, const u8 idx = 0) :
value{ static_cast<T>( val ) }, previous_value{ 0 }, bits{ value }
{}
template<typename... Args>
Register(Args... args) {}
template<typename U>
Register(const Register<U>& reg, const u8 idx = 0) : Register(reg.value, idx) {}
void changeEndian() {
T tmp = value;
char* const p = reinterpret_cast<char*>(&tmp);
for (size_t i = 0; i < sizeof(T) / 2; ++i)
std::swap(p[i], p[sizeof(T) - i - 1]);
bits = tmp;
}
Register& operator=(const Register& obj) {
this->value = obj.value;
this->previous_value = obj.previous_value;
this->bits = obj.bits;
return *this;
}
template<typename Lhs, typename Rhs>
friend auto operator+(const Register<Lhs>& l, const Register<Rhs>& r);
};
} // namespace vpc
【问题讨论】:
标签: c++ templates operator-overloading c++17 auto