Исходное сообщение отредактировано для обеспечения более минимального воспроизведения.
Давний программист на C здесь, заново изучаю C++ впервые примерно с 2010 года, так что я совсем новичок во всем современном C++11 и выше. Я думаю, что понимаю намерение конструкторов копирования и перемещения и почему их можно явно определить/по умолчанию/удалить. Однако я совершенно не понимаю взаимодействия между различными типами конструкторов в моей собственной учебной программе:
.TerritoryUSA.hpp
#ifndef TERRITORYUSA_HPP
#define TERRITORYUSA_HPP
namespace TerritoryUSA {
enum class Identifier {
Alabama,
Alaska,
American_Samoa
};
class Territory {
private:
Identifier identifier;
public:
// Construct.
Territory(Identifier identifier);
//Territory(Territory& other) = delete; // 1
//Territory(Territory&& other) = delete; // 2
// Move.
//Territory& operator=(Territory& other) = delete; // 3
//Territory& operator=(Territory&& other) = delete; // 4
// Destruct.
~Territory() = default;
// Methods.
Identifier getIdentifier();
};
}
#endif /* TERRITORYUSA_HPP */
TerritoryUSA.cpp
#include "TerritoryUSA.hpp"
using namespace TerritoryUSA;
// Construct.
Territory::Territory(Identifier identifier) : identifier{identifier} {}
// Methods.
Identifier Territory::getIdentifier() {
return identifier;
}
main.cpp
#include <cstdio>
#include "TerritoryUSA.hpp"
using namespace TerritoryUSA;
int main() {
Territory territories[]{
Territory(Identifier::Alabama),
Territory(Identifier::Alaska),
Territory(Identifier::American_Samoa)
};
for (Territory territory : territories) {
printf("%d\n", static_cast<int>(territory.getIdentifier()));
}
}
выход
0
1
2
Program ended with exit code: 0
Обратите внимание, что это делается в Xcode на MacOS, с чем я также пытаюсь разобраться, так как при написании C я в основном работаю с vim/make в Linux. Я не понял, где найти версию компилятора. пока, но это совершенно новый обновленный Macbook со свежеустановленным Xcode, поэтому он не может быть очень старым. Все, что я сделал, это запустил новое приложение Xcode «инструмент командной строки» и поместил 3 файла в каталог проекта, используя меню правой кнопки мыши, поэтому система сборки должна быть правильной (и она успешно собирается и работает, когда отмеченные строки прокомментированы ).
Часть, которая чертовски сбивает меня с толку, заключается в том, что когда я выборочно раскомментирую строки, отмеченные 1-4 в файле заголовка, я получаю различные ошибки, связанные с построением объектов Territory. Вот результаты, которые я получаю при раскомментировании отдельных строк:
Строка 1:
Строки 8-10 файла main.cpp (где я пытаюсь создать объекты Territory) я получаю эту красную ошибку:
No matching constructor for initialization of 'TerritoryUSA::Territory'
Он также выделяет серым цветом строку 17 hpp (конструктор, который принимает аргумент идентификатора) с этим сообщением:
1. Candidate constructor not viable: no known conversion from 'TerritoryUSA::Territory' to 'TerritoryUSA::Identifier' for 1st argument
Он также выделяет незакомментированную строку 1 серым цветом с этим сообщением:
2. Candidate constructor not viable: expects an l-value for 1st argument
Строка 2:
Строки 8-10 файла main.cpp (где я пытаюсь создать объекты Territory) я получаю эту красную ошибку:
Call to deleted constructor of 'TerritoryUSA::Territory'
Он также выделяет незакомментированную строку 2 серым цветом с этим сообщением:
1. 'Territory' has been explicitly marked deleted here
Строка 3:
Ошибок нет
Строка 4:
Строки 8-10 файла main.cpp (где я пытаюсь создать объекты Territory) я получаю эту красную ошибку:
Call to implicitly-deleted copy constructor of 'TerritoryUSA::Territory'
Он также выделяет незакомментированную строку 4 серым цветом с этим сообщением:
1. Copy constructor is implicitly deleted because 'Territory' has a user-declared move assignment operator
Вопросы
Для строки 1, почему она пытается использовать конструктор без аргументов и/или делать какие-либо неявные предположения? Разве я не предоставил совершенно правильный явный конструктор для подписи с 1 аргументом идентификатора? Я намеренно не хочу позволять пользователям создавать экземпляры объектов Territory без каких-либо аргументов (откровенно говоря, я не хочу, чтобы они создавали их вообще, но шаг за шагом). И еще, какое отношение к этому имеет удаление конструктора копирования? Я не пытаюсь ничего копировать.
Для строки 2, опять же, я понимаю, что удалил конструктор по умолчанию, но я не пытаюсь использовать его, я пытаюсь использовать тот, который принимает идентификатор. Почему я сбиваюсь с толку? И еще, какое отношение к этому имеет удаление конструктора перемещения? Я ничего не пытаюсь переместить.
Что касается строки 4, почему я получаю сообщение об ошибке, связанное с удаленным конструктором копирования, если я удаляю оператор перемещения? Я совершенно запутался в этом вопросе, особенно потому, что в строке 3 не было ошибок.
Постарайтесь сделать фрагмент вашего примера минимальным; перечисление должно иметь только несколько членов, чтобы вы могли продемонстрировать поведение.
Не могу воспроизвести. Какой компилятор? Какой стандарт? Как выглядит ваш файл исходный (CPP)?
Можете ли вы попробовать определить статический массив в файле cpp, а не в заголовке? Как предлагается здесь: coderhelper.com/a/2117331/260313
Есть ли какая-то особая причина, по которой вы создаете член enum в списке инициализаторов конструктора с фигурными скобками вместо круглых скобок? Другими словами, зачем делать это
Territory(Identifier identifier) : identifier{identifier} {}
вместо этого?Territory(Identifier identifier) : identifier(identifier) {}
@Joe Почему бы не использовать синтаксис инициализации скобок?
Я пытаюсь обновить свой пример, чтобы сделать его немного проще, но имейте в виду, что я слежу за книгой и все еще довольно рано, поэтому я еще не очень хорошо знаком с тем, как правильно реорганизовать весь этот код. Чтобы ответить @Joe, книга, которую я читаю, называется «Ускоренный курс C++: быстрое введение» Джоша Лоспиносо, и в ней рекомендуется всегда использовать фигурную инициализацию. Я забыл точные примеры, но он предоставил несколько примеров пограничных случаев, когда круглые скобки работали не совсем правильно или были немного неинтуитивными. Для начала я пытаюсь выработать хорошие привычки, поэтому брекеты мне показались подходящим вариантом.
пожалуйста, прочитайте о минимально воспроизводимом примере. 2 или 3 значения перечисления было бы достаточно, чтобы продемонстрировать проблему
Этот код отлично компилируется: onlinegdb.com/Qeoqpoemm
Ваша ошибка, по-видимому, вызвана компиляцией clang для стандартов C++ 14 или более ранних версий. Для этих стандартов gcc выдает другое, но похожее сообщение об ошибке. При компиляции для C++17 ошибок не возникает. На какой стандарт вы собираетесь ориентироваться?
@JaMiT Это кажется вероятным, поскольку кто-то другой смог скомпилировать без проблем. Я намерен ориентироваться на последний стандарт (С++ 20, если я не ошибаюсь), если только нет веских причин не делать этого. На данный момент я просто слежу за книгой в учебных целях, поэтому не уверен, почему я буду нажимать что-то слишком новое синтаксически. Теперь, если бы я только мог понять, где, черт возьми, настройки компилятора находятся в Xcode...
@dboeger1 Помогает ли многие вопросы о различных компиляторах C++, доступных мне в OS X? Ему несколько лет, поэтому расположение опций могло измениться.