为什么要显式删除构造函数而不是将其设为私有?
Why explicitly delete the constructor instead of making it private?
何时/为什么我要显式删除构造函数?假设原因是为了防止其使用,为什么不让它private
呢?
class Foo
{
public:
Foo() = delete;
};
怎么样:
//deleted constructor
class Foo
{
public:
Foo() = delete;
public:
static void foo();
};
void Foo::foo()
{
Foo f; //illegal
}
对
//private constructor
class Foo
{
private:
Foo() {}
public:
static void foo();
};
void Foo::foo()
{
Foo f; //legal
}
它们基本上是不同的东西。private
告诉您,只有类的成员才能调用该方法或访问该变量(当然还有朋友)。在这种情况下,该类(或任何其他成员)的static
方法调用类的private
构造函数是合法的。这不适用于已删除的构造函数。
在这里采样。
为什么要显式删除构造函数?
另一个原因:
当我想确保使用初始值设定项调用类时,我会使用delete
。 我认为这是一种非常优雅的方式,无需运行时检查即可实现这一目标。
C++编译器会为您执行此检查。
class Foo
{
public:
Foo() = delete;
Foo(int bar) : m_bar(bar) {};
private:
int m_bar;
};
这个 -非常简化- 代码确保没有像这样的实例化(默认构造):Foo foo;
tl;博士
显式delete
始终有效,并提供更易于理解的错误消息。不应再使用私人声明来阻止呼叫!
详细说明
如果声明函数私有,则仍然可以调用它。例如,对于构造函数,来自static
或friend
函数。
通过显式删除函数,您可以表示永远不能使用它。这尤其会导致在尝试调用此类函数时出现清晰易懂的错误消息。
在下面的类中,我们没有默认构造函数,因为没有有意义的方法来实现它。该类包含一个引用,它需要一些对象来指向。
class Foo {
private:
int& ref_;
public:
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:9: error: no matching function for call to ‘Foo::Foo()’
10 | Foo();
| ^
main.cpp:6:5: note: candidate: ‘Foo::Foo(int&)’
6 | Foo(int& ref) : ref_(ref) {}
| ^~~
main.cpp:6:5: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
1 | class Foo {
| ^~~
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
删除构造函数会使错误消息简短且易于理解。
class Foo {
private:
int& ref_;
public:
Foo() = delete;
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:11:9: error: use of deleted function ‘Foo::Foo()’
11 | Foo();
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
如果我们通过私有声明(没有定义,这是不可能的)来解决这个问题,消息一开始看起来很相似。
class Foo {
private:
int& ref_;
Foo();
public:
Foo(int& ref) : ref_(ref) {}
};
int main() {
Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:12:9: error: ‘Foo::Foo()’ is private within this context
12 | Foo();
| ^
main.cpp:5:5: note: declared private here
5 | Foo();
| ^~~
只要您不从可访问类的私有部分的上下文中调用构造函数,这就可以很好地工作。如上所述,这可以是例如静态或友好函数。原则上,它也可以是正常功能,尽管这种用例相当罕见。
class Foo {
private:
int& ref_;
Foo();
public:
Foo(int& ref) : ref_(ref) {}
static Foo create() {
return Foo(); // compiles fine
}
void foo() {
Foo(); // compiles fine
}
friend void bar();
};
void bar() {
Foo(); // compiles fine
}
int main() {}
g++ -c -o main.o main.cpp
这编译完全没有问题,编译只是假设其他地方会有Foo::Foo()
的定义。一旦链接器必须从中生成可执行文件,它就会报告缺少的定义。
$ g++ main.o
# or as one step with compilation and linking
$ g++ main.cpp
/usr/bin/ld: /tmp/ccnhLDsv.o: in function `bar()':
main.cpp:(.text+0x23): undefined reference to `Foo::Foo()'
collect2: error: ld returned 1 exit status
此类错误通常非常难以调试,因为它们卡在代码库中的任何位置,并且您不知道错误在哪个文件,更不用说在哪一行了。
另一方面,显式delete
在错误所在的三个位置提供三条精确的错误消息。
class Foo {
private:
int& ref_;
public:
Foo() = delete;
Foo(int& ref) : ref_(ref) {}
static Foo create() {
return Foo(); // error
}
void foo() {
Foo(); // error
}
friend void bar();
};
void bar() {
Foo(); // error
}
int main() {}
$ g++ main.cpp
main.cpp: In static member function ‘static Foo Foo::create()’:
main.cpp:10:20: error: use of deleted function ‘Foo::Foo()’
10 | return Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
main.cpp: In member function ‘void Foo::foo()’:
main.cpp:14:13: error: use of deleted function ‘Foo::Foo()’
14 | Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
main.cpp: In function ‘void bar()’:
main.cpp:21:9: error: use of deleted function ‘Foo::Foo()’
21 | Foo(); // error
| ^
main.cpp:6:5: note: declared here
6 | Foo() = delete;
| ^~~
其他信息
void foo(int need_integer) {}
int main() {
foo(5.4); // might trigger a warning, but compiles
}
请注意,delete
也可用于正常功能。例如,防止隐式转换。
void foo(int need_integer) {}
void foo(double) = delete;
int main() {
foo(5); // okay
foo(5.4); // error
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:6:8: error: use of deleted function ‘void foo(double)’
6 | foo(5.4); // error
| ~~~^~~~~
main.cpp:2:6: note: declared here
2 | void foo(double) = delete;
| ^~~
我在LLVM的源代码中遇到了默认的ctor声明为"已删除"(例如在AlignOf.h中)。关联的类模板通常位于名为"llvm::d etail"的特殊命名空间中。我认为那里的全部目的是他们只将那个类视为一个辅助类。他们从未打算实例化它们;仅在其他类模板的上下文中使用它们,并在编译时运行一些元编程技巧。
例如,有这个 AlignmentCalcImpl 类模板,它仅在另一个名为 AlignOf 的类模板中使用,作为 sizeof(.) 运算符的参数。该表达式可以在编译时计算;并且不需要实例化模板 ->那么为什么不声明默认的 ctor delete 来表达这个意图。
但这只是我的假设。
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- std::ofstream 作为类成员删除复制构造函数?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- C++ 尝试在不存在的构造函数中引用已删除的函数(使用 rapidJson)
- 具有已删除移动和复制构造函数的类的就地构造
- 聚合初始化和删除的复制构造函数,也称为不可复制的 obejcts 作为字段
- 在引用初始化中使用已删除的复制构造函数进行复制初始化
- 在运行时有条件地删除类成员或跳过调用该成员对象的构造函数
- 我的类中几乎所有的构造函数和解构函数都被隐式定义为已删除?
- 为什么在删除"移动构造函数"时使用"复制构造函数"?
- 调用类模板中隐式删除的复制构造函数
- 当类型适当的构造函数可用时,为什么一个编译器尝试使用已删除的副本构造函数
- 不带初始值设定项的构造函数列表,其中包含带有已删除构造函数的对象
- 是否可以实例化具有已删除构造函数和析构函数的非聚合类?
- 为什么编译器在试图初始化具有C 11样式的对象数组时隐含删除构造函数
- 调用的已删除构造函数
- 为什么要显式删除构造函数而不是将其设为私有?
- 如何创建此已标记联合的实例?关于已删除构造函数的编译器错误