解决由于不变成员导致的编译器错误,并带有可能删除的默认构造函数 [英] Resolving a compiler error due to an invariant member with a possible deleted default constructor

查看:176
本文介绍了解决由于不变成员导致的编译器错误,并带有可能删除的默认构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have asked a series of questions that all relate to the same source code in this order:

  1. experimenting-with-unions-and-bitfields在结构和模板内
  2. > try-to-flip-the-order-of -bits-in-stdbitset
  3. 避免-ambiguity-in-overload-resolution
  1. experimenting-with-unions-and-bitfields-within-a-structures-and-templates
  2. trying-to-flip-the-order-of-bits-in-stdbitset
  3. avoiding-ambiguity-in-overload-resolution

我还在代码审查"中问了一系列与此相关的问题.

I've also asked these series of questions over at Code Review that are also related.

  1. emulation-virtual-registers-by联合使用位域结构和模板专业化
  2. emulating-virtual-registers-part-2
  1. emulating-virtual-registers-by-experimenting-with-unions-bitfields-structs-and-template-specialization
  2. emulating-virtual-registers-part-2

这应该为您提供我原始代码设计的概述,并在那里供参考和背景信息.从那时起,我开始研究我的工作代码,并想进一步简化它.

This should give you an overview of my original code design and it is there for reference and background information. Since then I started to look at my working code and wanted to simplify it even more.

然后,我决定删除模板的特殊性,并让我的Register类的宽度为64位,而不是默认情况下的8位,同时专门处理更高阶的寄存器.

I then decided to remove the template specializations and have my Register class be 64 bits wide instead of 8 bits by default while specializing higher order sized registers.

我正在尝试合并一个想法,即可以通过以下任何一种方式访问​​数据:

I'm trying to incorporate the idea that one can access the data in any of these manners:

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

通过实用程序使用std::bitset,可以轻松访问全64位寄存器中的任何位.通过使用并集,我应该能够正确地映射内存,以便可以将寄存器表示为以下任意组合,并可以通过以下任意组合进行访问:

And through the utility use of std::bitset one can easily access any of the bits within the full 64 bit register. With the use of unions I should be able to correctly map the memory so that a register can be represented as and accessed by any of these combinations:

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

使用并集的概念是在内存中具有单个空间,该空间代表任何给定寄存器的64位.现在,我试图使我的Register类保持微不足道的可复制性.

The concept of using the union is to have a single space in memory that represents 64 bits for any given register. Now I'm trying to keep my Register class trivially copyable.

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

So I had modified the original code from what can be found throughout the links above to a simpler version of this:

注册.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

然后我要进行测试,以确保到目前为止所有内容都是可复制的.所以我运行了这个简短的程序.

Then I want to test to make sure that everything so far is trivially copyable. So I run this short program.

main.cpp

#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

现在,当查看联合位时,它表明它是可复制的.因此,与其在结构中声明Bits类型的变量作为其数据成员,不如将其声明为数据成员.我相信我们应该能够在我们的结构中拥有一个匿名联合,以便我们可以直接使用我们的qword,dwords,单词和字节.所以现在该类将如下所示:

Now when looking at the union Bits it states that it is trivially copyable. So instead of declaring a variable of type Bits in the struct as its data member; I believe we should be able to have an anonymous union within our structure so we can get right to our qword, dwords, words and bytes directly. So now the class will look like this:

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

    Register() = default;
};

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

Then I run this line of code in our 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中声明了函数原型:

Now I'm working on my function that operates on a Register object. It will reverse the order of the bits as seen from the previously asked questions. Except in this case I'm not using templates. So here I declare the function prototype in Register.h after my class:

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

然后创建一个Register.cpp文件只是为了实现此功能.

And then I created a Register.cpp file just to implement this function.

Register.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".然而;我在使用语言设置设为最新的草稿标准或标志(/std:c++latest)的Visual Studio 2017上出现此编译器错误.

So now that I have my function written I clean my solution and now I try to compile "Register.h". However; I'm getting this compiler error frpm Visual Studio 2017 with the language setting set to latest draft standard or flag (/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的上方时,会显示以下消息:

So when I click on error C2280 it directs me to the declaration of my Register variable cpy. When I move my mouse cursor overtop of the variable cpy it gives me the message:

vpc ::注册cpy

默认引用的"vpc :: Register"构造函数无法引用-这是一个已删除的函数

vpc::Register cpy

the default constructor of "vpc::Register" cannot be referenced -- it is a deleted function

所以我的问题变成了:如果以前的所有内容都是可复制的,为什么要删除默认构造函数?由于我现在在函数中使用它,所以突然间,它表明我的结构具有不变成员,该成员没有琐碎的可复制构造函数,并且它指向Register::value作为罪魁祸首.是什么原因造成的,如何以及为什么?我该怎么做才能解决或解决此问题?

So my question becomes: Why is the default constructor being deleted if everything from before was trivially copyable? Since I am now using it within a function, all of a sudden, it is stating that my structure has an invariant member that doesn't have a trivially copyable constructor and it is pointing to Register::value as the culprit. What is causing this, how and why? What can I do to fix or resolve this?

推荐答案

以下是简短的复制内容:

Here's a shorter reproduction:

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

普通可复制的要求实际上并未检查默认构造函数-只是关于复制/移动构造函数/赋值.这是一条红鲱鱼.让我们看一下默认构造函数规则:

The trivially copyable requirement doesn't actually check the default constructor - it's just about the copy/move constructor/assignment. That's a red herring here. Let's look at the default constructor rules:

如果满足以下条件,则将X类的默认默认构造函数定义为已删除:

A defaulted default constructor for class X is defined as deleted if:

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

在我们的X中,我们有一个具有非平凡默认构造函数的变体成员(用户提供了T(),因此它不是平凡的……而std::bitset的默认构造函数实际上在执行某些操作),因此默认构造函数定义为已删除.在此示例中,默认构造函数是隐式默认的-在OP中它是默认默认的-但效果是相同的.

In our X, we have a variant member with a non-trivial default constructor (T() is user provided, hence it's non-trivial... and std::bitset's default constructor actually does something), so the default constructor is defined as deleted. In this example, the default constructor is implicitly defaulted - in the OP it's explicitly defaulted - but the effect is the same.

解决方法是提供一个默认构造函数,该构造函数可以执行以下操作...您希望默认构造函数实际执行的操作是什么:

The workaround is to provide a default constructor that does... whatever it is you want the default constructor to actually do:

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

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

The rule of thumb here is that union special members are only implicitly provided if all the variants are trivial.

这篇关于解决由于不变成员导致的编译器错误,并带有可能删除的默认构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆