谈谈explicit关键字

作者在公司代码中看到大量explicit关键字,老版MSDN例子不完善,后从网上文章得知explicit用于防止隐式使用拷贝构造函数,还附上新版MSDN例子及网上文章内容。

今天看到公司的代码内有大量的explicit关键字,但是老版的MSDN内例子并不完善,实在是不明白,最终从网上一篇文章内找到了答案:原来explicit是为了防止隐式使用拷贝构造函数的.以下附上从新版MSDN中找到的例子和网上那篇文章:

// Copy From MSDN

This keyword is a declaration specifier that can only be applied to in-class constructor declarations. An explicit constructor cannot take part in implicit conversions. It can only be used to explicitly construct an object.

The following program will fail to compile because of the explicit keyword. To resolve the error, remove the explicit keywords and adjust the code in g.

// spec1_explicit.cpp
// compile with: /EHsc
#include 
 
  
class C 
{
public:
   int i;
   explicit C(const C&)   // an explicit copy constructor
   {
      printf("/nin the copy constructor");
   }
   explicit C(int i )   // an explicit constructor
   {
      printf("/nin the constructor");
   }


   C()
   {
      i = 0;
   }
};

class C2
{
public:
   int i;
   explicit C2(int i )   // an explicit constructor
   {
   }
};

C f(C c)
{   // C2558
   c.i = 2;
   return c;   // first call to copy constructor
}

void f2(C2)
{
}

void g(int i)
{
    f2(i);   // C2558
   // try the following line instead
   // f2(C2(i));
}

int main()
{
   C c, d;
   d = f(c);   // c is copied
}
 
Note    explicit on a constructor with multiple arguments has no effect, since such constructors cannot take part in implicit conversions. However, for the purpose of implicit conversion, explicit will have an effect if a constructor has multiple arguments and all but one of the arguments has a default value.

 

// Copy From Internet Article

Pointer
不看书不知道自己的C++有多差T_T,看到 explicit 时遇到一个问题。请看下面一段程序:
class A{
public:
A(int i) : m_i(i){}
int m_i;
};
int main(){
A a = 0;
a = 10; // 这里是什么操作?
}

这个操作产生了一个临时对象。
我怀疑是默认赋值运算符 “A &operator = (int i){}”,于是重载了一下该运算符,结果确实运行到重载的运算符函数里了,那么临时对象又是如何产生的呢?

难道默认的赋值运算符是这样操作的?
A &operator = (int i){
A a(i);
return a;
}

这让我想起了类似的函数操作:
void fn(A a){
// ...
}
这里可以直接写fn(10);也是产生了一个临时对象。

难道真的是这样吗?忘解惑。

alexeyomux
老兄你用的是哪个编译器?在我印象之中,好像标准C++并不会给出operator =啊。等我去试一试。

gongminmin
当然会有默认的operator=了
按照c++标准,编译器会生成五个默认成员函数:
默认构造函数
拷贝构造函数
析构函数
operator=
operator&
 
千里马肝
class A
{
public:
A(int i) : m_i(i){}
int m_i;
};

分别说说吧:
1. A a = 0;
首先, compiler认为这样写是不符合规矩的, 因为A = A才是正常行为。
但是她并不放弃, 通过搜索, 发现A可以根据一个int构造, 同时这个A(int i)没有用explicit修饰过。
那么A a = 0; 这样的一句话随即转变成:
A tmp(0);
A a = tmp;
需要说明的是, A a = tmp是调用的copy ctor, 虽然class A中并没有, 但是通常不写copy ctor的话,
compiler都会生成一个memberwise assignment操作性质的ctor, 底层实现通常会以memcpy进行。

2. a = 10;
首先, 这样同ctor的情况一样, compiler无法直接进行操作。
类推, 等同于代码:
A tmp(10);
a = tmp;
需要注意的是, a = tmp是调用的assignment操作, 同ctor一样,我们自己不写, 编译器同样进行
memberwise assignment操作。

3. fn(A a)
同样, fn(10)也是不对的, 但是"按照惯例", 呵呵, 会有:
A tmp(10);
fn(tmp);

另外, 为你解惑:
copy ctor的写法只能是T::T(const T &);
而assignment的写法可以多变, 即任意. 以T为例,
可以有
T &operator = (int n);
也可有
T &operator = (const char *);
当然, 你要确认如此的定义是对T而言有意义.

然后, 上述a = tmp, 即调用的默认的、标准的、自动生成的T &operator = (const T &).
开销是会有一个临时的A tmp生成, 然后memcpy.
但如果你自已写了T &operator = (int n), 那么a = 10即意味着a.m_i = 10.
当然, 以开销而言要视你的T &operator = (int n)是否为inline了.

对于explicit, 当修饰explicit A(int i) : m_i(i){}, 那么即告诉compiler不要在私底下做那么多的转换动作.
而且自动生成如A tmp(0)这样的东西是我们不想要的, 因为某些情况下自动转换这种行为是错误的.

最后, 相关此类问题, 还有一个话题, 即class A可以有operator int(), 会在
fn(int n){}
A a(3);
fn(a)
起到魔术般的作用. 关于这个, 留给你自己看看书吧:)

最后,祝学习C++的路上一帆风顺。Good luck~
<think>我现在需要详细解释C++中的explicit关键字。首先,explicit关键字主要用于构造函数,防止隐式类型转换。当构造函数被声明为explicit时,只能通过直接初始化或显式转换来调用,不能进行隐式转换。 首先,我需要明确explicit的作用对象。它只能用于单参数构造函数,或者具有默认参数的多参数构造函数(因为可以视为单参数)。例如,如果一个构造函数接受一个参数,或者有多个参数但除了一个之外都有默认值,那么使用explicit可以防止编译器进行隐式转换。 接下来,我应该讨论为什么需要explicit。在C++中,编译器有时会自动进行类型转换,比如当需要一个对象而传递了一个可以转换为该类型的参数时。这可能导致意外的行为,特别是在有多个构造函数或运算符重载的情况下。使用explicit可以避免这种隐式转换,提高代码的可读性和安全性。 然后,我需要举一些例子来说明explicit的用法和效果。例如,一个类有explicit构造函数时,不能通过赋值的方式初始化,而必须使用直接初始化或显式转换。此外,在函数参数传递时,如果参数类型与构造函数参数类型不匹配,explicit会阻止隐式转换,导致编译错误。 另外,还应该提到explicit在拷贝构造函数中的应用。虽然拷贝构造函数通常不需要explicit,但在某些情况下,比如禁止隐式拷贝,或者配合delete使用,可能也会用到。不过,C++11之后,explicit也支持拷贝构造函数的显式控制,但这种情况较少见。 还需要讨论explicitC++11及以后版本中的扩展,比如explicit与转换运算符的结合使用,以及explicit在常量表达式中的应用。例如,C++11允许在转换运算符前使用explicit,这样只有在直接初始化或显式转换时才会调用该运算符,防止隐式转换导致的意外行为。 此外,应该提到explicit的替代方案,比如使用带有一个参数的构造函数而不加explicit,但这样可能会导致隐式转换的风险。因此,在设计中需要根据具体情况权衡是否使用explicit。 最后,需要总结explicit的主要用途和最佳实践,强调在何种情况下应该使用explicit,以及不使用可能带来的问题。同时,可以结合实际编程中的例子,说明explicit如何提高代码的清晰度和安全性。 在回答完问题后,我需要根据用户的问题和我的回答,提出3-5个相关问题。这些问题应该围绕explicit关键字的不同方面,比如使用场景、与隐式转换的区别、在特定情况下的行为等。需要确保这些问题与回答内容紧密相关,并且能够引导进一步思考或讨论。</think>`explicit` 是 C++ 中用于限制构造函数/转换运算符隐式转换的关键字,核心作用是**禁止编译器进行自动类型推导和隐式类型转换**,强制要求显式初始化或类型转换。 ### 核心解读 1. **构造函数场景** - 当构造函数参数为单参数(或含默认参数的多参数)时,若未加 `explicit`,编译器允许隐式转换(如 `MyClass obj = 10;`)。 - 添加 `explicit` 后,只能通过直接初始化(`MyClass obj(10);`)或显式转换(`static_cast<MyClass>(10)`)调用,禁止 `= ` 形式的隐式转换。 2. **转换运算符场景** - C++11 起支持在转换运算符前加 `explicit`(如 `explicit operator int() const`),仅允许在直接初始化或显式转换时触发,避免意外隐式转换(如 `if(obj)` 自动调用转换)。 3. **设计意图** - 提升代码可读性:明确区分构造与类型转换,避免“意外构造”引发的逻辑错误(如字符串与字符的二义性)。 - 增强安全性:防止编译器“自作聪明”的隐式转换导致性能损耗或安全隐患(如 `std::string s = 'a';` 实际构造长度为1的字符串)。 ### 典型应用示例 ```cpp class Vector2D { public: explicit Vector2D(double x, double y) : x_(x), y_(y) {} // 禁止隐式构造 explicit operator bool() const { return x_ != 0 || y_ != 0; } // 禁止隐式转bool }; // 合法用法 Vector2D v1(3.0, 4.0); // 直接初始化 auto v2 = static_cast<Vector2D>(5.0); // 显式转换 // 非法用法 Vector2D v3 = {6.0, 7.0}; // 隐式初始化被禁止 if (v1) { /*...*/ } // 隐式转bool被禁止 ``` ### 性能与安全性影响 - **性能优化**:避免不必要的临时对象构造(如隐式转换链),尤其在循环或高频调用场景。 - **错误预防**:阻止“窄化隐式转换”(如 `long` 转 `int` 的数据丢失)和“语义混淆”(如 `std::ofstream` 的 `operator<<(bool)` 与字符串的歧义)。 ### 扩展特性 - **C++11 后**:`explicit` 可用于标记构造函数为 `constexpr`,支持常量表达式场景。 - **C++20 起**:允许在模板构造函数中使用 `explicit`,增强泛型编程的控制力。
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值