【发布时间】:2021-08-08 21:56:17
【问题描述】:
对原始帖子进行了编辑,以提供更少的复制。
长期 C 程序员,自 2010 年以来第一次重新学习 C++,所以我对所有现代 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
请注意,这是在 MacOS 上的 Xcode 中完成的,我也在苦苦思索,因为我在编写 C 时主要在 Linux 上使用 vim/make。我还没有弄清楚在哪里可以找到编译器版本然而,但这是一个全新的更新的 Macbook,新安装了 Xcode,所以它不会很旧。我所做的只是启动一个新的 Xcode“命令行工具”应用程序并使用右键菜单将 3 个文件放在项目目录中,因此构建系统应该是正确的(并且当标记的行被注释时它确实构建并运行成功)。
让我感到困惑的部分是,当我有选择地取消注释头文件中标记为 1-4 的行时,我得到了与 Territory 对象的构造相关的各种错误。以下是取消注释单行时得到的结果:
第 1 行:
main.cpp 的第 8-10 行(我尝试实例化 Territory 对象的地方)我收到这个红色错误:
No matching constructor for initialization of 'TerritoryUSA::Territory'
它还在 hpp 的灰色第 17 行(接受标识符参数的构造函数)中突出显示以下消息:
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 行:
main.cpp 的第 8-10 行(我尝试实例化 Territory 对象的地方)我收到这个红色错误:
Call to deleted constructor of 'TerritoryUSA::Territory'
它还用这条消息以灰色突出显示未注释的第 2 行:
1. 'Territory' has been explicitly marked deleted here
第 3 行:
没有错误
第 4 行:
main.cpp 的第 8-10 行(我尝试实例化 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 行没有任何错误。
【问题讨论】:
-
尽量让你的示例 sn-p 最小化;枚举只需要几个成员就可以展示行为。
-
无法复制。什么编译器?什么标准?您的 source (CPP) 文件是什么样的?
-
你可以尝试在cpp文件中定义静态数组,而不是在标题中?如此处建议:stackoverflow.com/a/2117331/260313
-
您在构造函数初始化器列表中使用花括号而不是圆括号构造枚举成员有什么特别的原因吗?换句话说,为什么用这个
Territory(Identifier identifier) : identifier{identifier} {}而不是这个?Territory(Identifier identifier) : identifier(identifier) {} -
我正在尝试更新我的示例以使其更简单一些,但请记住,我正在关注一本书,而且还很早,所以我并不熟悉如何正确地重构所有这些代码。要回答@Joe,我正在阅读的书是 Josh Lospinoso 的“C++ Crash Course: A Fast-Paced Introduction”,它建议始终使用支撑初始化。我忘记了确切的例子,但它提供了一些极端情况的例子,括号不能很好地工作或有点不直观。我正在努力养成良好的开始习惯,所以牙套似乎是要走的路。
标签: c++ copy-constructor move-constructor