0

我已经按此顺序提出了一系列与相同源代码相关的问题:

  1. 在结构和模板中试验联合和位域
  2. 试图翻转stdbitset中的位顺序
  3. 避免重载解析中的歧义

我还在 Code Review 上提出了一系列相关的问题。

  1. 通过实验与联合位域结构和模板专业化模拟虚拟寄存器
  2. 模拟虚拟寄存器第 2 部分

这应该可以让您大致了解我的原始代码设计,并提供参考和背景信息。从那时起,我开始查看我的工作代码,并希望进一步简化它。

然后我决定删除模板特化,让我的寄存器类默认为 64 位宽而不是 8 位,同时专门化高阶大小的寄存器。

我试图将人们可以通过以下任何一种方式访问​​数据的想法结合起来:

  • 完整值: - 整个 64 位 qword
  • 半值: - 2 个单独的 32 位双字
  • 四分之一值 - 4 个单独的 16 位字
  • 第八个值 - 8 个单独的 8 位字节

并且通过实用程序的使用,std::bitset可以轻松访问完整 64 位寄存器中的任何位。通过使用联合,我应该能够正确映射内存,以便可以将寄存器表示为并由以下任何组合访问:

  • std::bitset<64> qword
  • std::bitset<32> dword[2]
  • std::bitset<16> word[4]
  • std::bitset<8> byte[8]

使用联合的概念是在内存中有一个空间,代表任何给定寄存器的 64 位。现在我试图让我的 Register 类可以轻松复制。

因此,我将上面链接中的原始代码修改为更简单的版本:

寄存器.h

#include <algorithm>
#include <bitset>
#include <string>
#include <vector>

namespace vpc {
    typedef std::int8_t  i8;
    typedef std::int16_t i16;
    typedef std::int32_t i32;
    typedef std::int64_t i64;

    const std::uint16_t BYTE = 0x08;
    const std::uint16_t WORD = 0x10;
    const std::uint16_t DWORD = 0x20;
    const std::uint16_t QWORD = 0x40;

    typedef std::bitset<BYTE>  Byte;
    typedef std::bitset<WORD>  Word;
    typedef std::bitset<DWORD> DWord;
    typedef std::bitset<QWORD> QWord;

    union Bits {
        QWord value;
        DWord dword[2];
        Word  word[4];
        Byte byte[8];
    };

    struct Register {
        Bits bits;
        Register() = default;
    };       

} // namespace vpc

然后我想测试以确保到目前为止的所有内容都可以轻松复制。所以我运行这个短程序。

主文件

#include <iostream>
#include <type_traits>
#include "Register.h"

int main() {
    using namespace vpc;

    std::cout << std::boolalpha;

    std::cout << "std::bitset<64> is trivially copyable " 
        << std::is_trivially_copyable<std::bitset<64>>::value << '\n'
              << "QWord is trivially copyable "
        << std::is_trivially_copyable<QWord>::value << '\n'
              << "DWord is trivially copyable "
        << std::is_trivially_copyable<DWord>::value << '\n'
              << "Word is trivially copyable "
        << std::is_trivially_copyable<Word>::value << '\n'
              << "Byte is trivially copyable "
        << std::is_trivially_copyable<Byte>::value << '\n'
              << "Bits is trivially copyable "
        << std::is_trivially_copyable<Bits>::value << '\n'
              << "Register is trivially copyable "
        << std::is_trivially_copyable<Register>::value << '\n';

    return EXIT_SUCCESS;
}

我得到这个输出:

std::bitset<64> is trivially copyable true
QWord is trivially copyable true
DWord is trivially copyable true
Word is trivially copyable true
Byte is trivially copyable true
Bits is trivially copyable true
My Register is trivially copyable true

现在,当查看 union Bits 时,它声明它是可简单复制的。因此,不要将结构中的 Bits 类型的变量声明为其数据成员;我相信我们应该能够在我们的结构中拥有一个匿名联合,这样我们就可以直接访问我们的 qword、dwords、单词和字节。所以现在这个类看起来像这样:

struct Register {
    union {
        QWord value;
        DWord dword[2];
        Word  word[4];
        Byte  byte[8];
    };

    Register() = default;
};

然后我在我们的 main.cpp 中运行这行代码

// code...

std::cout << std::boolalpha;
std::cout << "Register is trivially copyable "
          << std::is_trivially_copyable<Register>::value << '\n';

// code...

我得到这个输出:

Register is trivially copyable true

好的,到目前为止一切都很好。

现在我正在处理对 Register 对象进行操作的函数。它将反转从先前提出的问题中看到的位顺序。除了在这种情况下,我没有使用模板。Register.h所以在这里我在我的课后声明函数原型:

Register reverseBitOrder( Register& reg, bool copy = false );

然后我创建了一个Register.cpp文件来实现这个功能。

注册.cpp

#include "Register.h"

namespace vpc {

    Register reverseBitOrder(Register& reg, bool copy) {
        auto str = reg.value.to_string();
        std::reverse(str.begin(), str.end());

        if (copy) { // return a copy
            Register cpy;
            cpy.value = QWord(str);
            return cpy;
        } else {
            reg.bits.value = QWord(str);
            return { 0 };
        }
    }

} // namespace vpc

所以现在我已经编写了函数,我清理了我的解决方案,现在我尝试编译“Register.h”。然而; 我收到此编译器错误 frpm Visual Studio 2017 ,语言设置设置为最新的草案标准或标志(/std:c++latest)

--- Build started: Project: Corgi64, Configuration: Debug Win32 ------
1>Register.cpp
1>c:\***\register.cpp(10): error C2280: 'vpc::Register::Register(void)': attempting to reference a deleted function
1>c:\***\register.h(40): note: see declaration of 'vpc::Register::Register'
1>c:\***\register.h(40): note: 'vpc::Register::Register(void)': function was implicitly deleted because 'vpc::Register' has a variant data member 'vpc::Register::value' with a non-trivial default constructor
1>c:\***\register.h(34): note: see declaration of 'vpc::Register::value'
1>Done building project "Corgi64.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

因此,当我单击错误 C2280 时,它会将我引导至我的寄存器变量的声明cpy。当我将鼠标光标移到变量上方时,cpy它会给我以下消息:

vpc::注册cpy

“vpc::Register”的默认构造函数不能被引用——它是一个被删除的函数

所以我的问题变成了:如果之前的所有内容都可以轻松复制,为什么要删除默认构造函数?因为我现在在一个函数中使用它,突然之间,它表明我的结构有一个不变的成员,它没有一个可简单复制的构造函数,并且它指向Register::value了罪魁祸首。是什么原因造成的,如何以及为什么?我能做些什么来修复或解决这个问题?

4

2 回答 2

2

这是一个较短的复制:

struct T {
    T() { }
};

union X {
    T t;
};

static_assert(std::is_trivially_copyable_v<T>); // fine
static_assert(std::is_trivially_copyable_v<X>); // fine

X x; // error: use of deleted function X::X()

微不足道的可复制要求实际上并没有检查默认构造函数——它只是关于复制/移动构造函数/赋值。这是一个红鲱鱼。让我们看一下默认的构造函数规则

类 X 的默认默认构造函数在以下情况下定义为已删除:

  • X 是一个联合,它有一个带有非平凡默认构造函数的变体成员,并且 X 的任何变体成员都没有默认成员初始化器,
  • [...]

在我们的X中,我们有一个带有非平凡默认构造函数的变体成员(T()是用户提供的,因此它是非平凡的......并且std::bitset' 的默认构造函数实际上做了一些事情),因此默认构造函数被定义为已删除。在此示例中,默认构造函数是隐式默认的 - 在 OP 中它是显式默认的 - 但效果是相同的。

解决方法是提供一个默认构造函数......无论您希望默认构造函数实际做什么:

union X {
    X() : t() { }
    T t;
};

这里的经验法则是,union只有在所有变体都是微不足道的情况下才隐式提供特殊成员。

于 2019-05-06T14:57:52.397 回答
-1

在阅读了用户 Barry 在他的回答中所说的内容之后,我回过头来查看我的代码,我能够做到这一点:

struct Register {
    Bits bits;
    Register() : value{0}{}
};

对于我的 Register 类,我将函数定义更改为:

MyRegister reverseBitOrder(MyRegister& reg, bool copy) {
    auto str = reg.value.to_string();
    std::reverse(str.begin(), str.end());

    if (copy) { // return a copy
        MyRegister cpy;
        cpy.value = QWord(str);
        return cpy;
    } else {
        reg.value = QWord(str);
        return {};
    }
}

现在我的代码编译得很好,我得到了预期的输出。

于 2019-05-07T18:20:05.760 回答