使用更广为接受的方式介绍 explicit 关键字
maokelong opened this issue · 2 comments
首先感谢 Repo 主维护这么棒的 Repo,好多同学都向我推荐了这个。在阅读过程中,我发现对 explicit 关键字的介绍可以做得更好一些。
之前介绍到「explicit 修饰的构造函数可用来防止隐式转换」,而在 cppreference 中介绍到 「指定构造函数或转换函数 (C++11 起)为显式,即它不能用于隐式转换和复制初始化。」。这个表述更好一些,因为 1)这是官方的翻译,接受范围更广;2)区分了用于构造函数和转换函数两种使用场景,而且区分了隐式转换和复制初始化两种编译器行为,更为严谨。
但这样说显然很僵硬,因此可以这样优化:「explicit 修饰构造函数时可防止复制初始化;修饰转换函数(C++11 起)时可防止隐式转换」。
然而这句话的后半句是有问题的,因为存在名为「按语境转换」的例外,这样的例外使得在 if 等需要隐式将右值转换为 bool 的场景下,即使转换函数被声明为 explicit 的,隐式转换仍会发生。考虑到 repo 主的风格是不喜欢放太多细节的东西,这句话可以这样说:「explicit 修饰构造函数时可防止复制初始化;修饰转换函数(C++11 起)时可防止部分隐式转换」。
另外示例部分也可以做相应调整,从而和上文表述相一致。直接从 cppreference 中拿过来是最简单的做法。至于放不放上述特例,则取决于你的偏好了。
感谢提出关于 explicit 的改进,学习了。
我之前没了解过按语境转换,对您说的「这样的例外使得在 if 等需要隐式将右值转换为 bool 的场景下,即使转换函数被声明为 explicit 的,隐式转换仍会发生。」不是很理解,请问是指如下例子中if (b1);
和bool b6(b1);
的意思吗?
struct A
{
A(int) { }
operator bool() const { return true; }
};
struct B
{
explicit B(int) {}
explicit operator bool() const { return true; }
};
void doA(A a) {}
void doB(B b) {}
int main()
{
A a1(1); // OK:直接初始化
A a2 = 1; // OK:复制初始化
A a3{ 1 }; // OK:直接列表初始化
A a4 = { 1 }; // OK:复制列表初始化
A a5 = (A)1; // OK:允许 static_cast 的显式转换
doA(1); // OK:允许从 int 到 A 的隐式转换
if (a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
bool a8 = static_cast<bool>(a1); // OK :static_cast 进行直接初始化
B b1(1); // OK:直接初始化
B b2 = 1; // 错误:被 explicit 修饰的构造函数不可以复制初始化
B b3{ 1 }; // OK:直接列表初始化
B b4 = { 1 }; // 错误:被 explicit 修饰的构造函数不可以复制列表初始化
B b5 = (B)1; // OK:允许 static_cast 的显式转换
doB(1); // 错误:被 explicit 修饰的构造函数不可以从 int 到 B 的隐式转换
if (b1); // OK:被 explicit 修饰的转换函数 B::operator bool() 可以从 B 到 bool 的按语境转换
bool b6(b1); // OK:被 explicit 修饰的转换函数 B::operator bool() 可以从 B 到 bool 的按语境转换
bool b7 = b1; // 错误:被 explicit 修饰的转换函数 B::operator bool() 不可以隐式转换
bool b8 = static_cast<bool>(b1); // OK:static_cast 进行直接初始化
return 0;
}
另外,explicit修饰构造函数也可以防止隐式转换,因此我修改如下:
- explicit 修饰构造函数时,可以防止隐式转换和复制初始化
- explicit 修饰转换函数时,可以防止隐式转换,但 按语境转换 除外
我对这方面了解的没这么细,描述或者例子中哪里不对望指正。
很棒了,只是「错误:被 explicit 修饰的构造函数不可以从 int 到 B 的隐式转换」这句话需要注意一下,阅读起来不大通顺。除此之外就没有建议了,这个 Issue 就关闭了哈~