Устранение ошибки компилятора из-за инвариантного члена с возможным удаленным конструктором по умолчанию - PullRequest
0 голосов
/ 06 мая 2019

Я задал ряд вопросов, относящихся к одному и тому же исходному коду в следующем порядке:

  1. экспериментируя с объединениями и битовыми полями в пределах a-структуры и шаблоны
  2. попытка перевернуть порядок битов в stdbitset
  3. избегание неоднозначности-in-overload-resolution

Я также задавал эти серии вопросов в Code Review, которые также имеют отношение.

  1. эмуляция виртуальных регистров путем экспериментирования с объединениями-битовыми полями-структурами-шаблонами-специализациями
  2. эмуляция виртуальных регистров-часть-2

Это должно дать вам краткий обзор моей оригинальной конструкции кода, и это там для справочной и справочной информации.С тех пор я начал смотреть на свой рабочий код и хотел еще больше упростить его.

Затем я решил удалить специализации шаблонов и сделать так, чтобы мой класс Register имел ширину 64 бита вместо 8 бит по умолчанию, а специализация более высокая.регистры размера заказа.

Я пытаюсь воплотить идею, что можно получить доступ к данным любым из следующих способов:

  • Полная стоимость: - все 64-битное слово
  • Половинное значение (я): - 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 легко копируемым.

Поэтому я изменил исходный код из того, что можно найти по ссылкам выше, до более простой версии этого:

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

Затем я хочу проверить, чтобы убедиться, что все до сих пор легко копируется.Итак, я запускаю эту короткую программу.

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 в структуре в качестве члена данных;Я считаю, что мы должны иметь возможность иметь анонимный союз в нашей структуре, чтобы мы могли получить право на наши слова, слова, слова и байты напрямую.Итак, теперь класс будет выглядеть так:

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 только для реализации этой функции.

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».Тем не мение;Я получаю эту ошибку компилятора для 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 :: Register cpy

На конструктор по умолчанию "vpc :: Register" нельзя ссылаться -- это удаленная функция

Таким образом, мой вопрос звучит так: почему конструктор по умолчанию удаляется, если все ранее было тривиально копируемым? Поскольку я теперь использую его внутри функции, внезапно я заявляю, что в моей структуре есть инвариантный член, который не имеет тривиально копируемого конструктора, и он указывает на Register::value как на виновника. Что вызывает это, как и почему? Что я могу сделать, чтобы это исправить или решить?

Ответы [ 2 ]

2 голосов
/ 06 мая 2019

Вот более короткое воспроизведение:

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 специальные члены предоставляются неявно, только если все варианты тривиальны.

0 голосов
/ 07 мая 2019

После прочтения того, что сказал пользователь Барри в своем ответе, и я вернулся, чтобы посмотреть свой код, я смог сделать следующее:

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 {};
    }
}

И теперь мой код компилируется нормально, и я получаю ожидаемые результаты.

...