What does the explicit keyword mean?
在C++中EDCOX1的0个关键字是什么意思?
编译器可以进行一次隐式转换以将参数解析为函数。这意味着编译器可以使用带有单个参数的可调用构造函数从一种类型转换为另一种类型,以便为参数获得正确的类型。
下面是一个带有可用于隐式转换的构造函数的示例类:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Foo { public: // single parameter constructor, can be used as an implicit conversion Foo (int foo) : m_foo (foo) { } int GetFoo () { return m_foo; } private: int m_foo; }; |
下面是一个简单的函数,它接受一个
1 2 3 4 | void DoBar (Foo foo) { int i = foo.GetFoo (); } |
这里是调用
1 2 3 4 | int main () { DoBar (42); } |
这个论点不是
编译器可以为每个参数执行一次此操作。
将
您可能希望这样做的原因是为了避免意外构造,从而隐藏错误。人为的例子:
- 您有一个
MyString(int size) 类,它有一个构造给定大小字符串的构造函数。您有一个函数print(const MyString&) ,您调用print(3) (当您实际上打算调用print("3") )。您希望它打印"3",但它会打印一个长度为3的空字符串。
假设您有一个类
1 2 3 4 5 | class String { public: String(int n); // allocate n bytes to the String object String(const char *p); // initializes object with char *p }; |
现在,如果您尝试:
1 | String mystring = 'x'; |
字符
1 2 3 4 5 | class String { public: explicit String (int n); //allocate n bytes String(const char *p); // initialize sobject with string p }; |
在C++中,只有一个所需参数的构造函数被认为是隐式转换函数。它将参数类型转换为类类型。这是否是一件好事取决于构造函数的语义。
例如,如果您有一个带有构造函数
另一方面,如果您有一个buffer类,其构造函数
1 | class Buffer { explicit Buffer(int size); ... } |
那样,
1 2 | void useBuffer(Buffer& buf); useBuffer(4); |
变成编译时错误。如果要传递临时
1 | useBuffer(Buffer(4)); |
总之,如果单个参数构造函数将参数转换为类的对象,则可能不想使用
这个答案是关于带有/不带有显式构造函数的对象创建的,因为其他答案中没有涉及它。
考虑不带显式构造函数的以下类:
1 2 3 4 5 6 7 8 9 10 | class Foo { public: Foo(int x) : m_x(x) { } private: int m_x; }; |
可以通过两种方式创建foo类的对象:
1 2 3 | Foo bar1(10); Foo bar2 = 20; |
根据实现的不同,实例化类foo的第二种方式可能会令人困惑,或者不是程序员想要的方式。将
通常将单参数构造函数声明为
还要注意的是,
- 所有参数的默认参数,或
- 第二个参数以后的默认参数
都可以用作单参数构造函数。所以你可能想把这些也做成
如果您有意不想让单参数构造函数显式化,例如,如果您正在创建一个函数(请查看此答案中声明的"add_x"结构)。在这种情况下,创建像
下面是一篇关于显式构造函数的好文章。
关键字
- 类X的构造函数,不能用于将第一个(仅限任何)参数隐式转换为类型X
C++ [class.conv.ctor]
1) A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.
2) An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or valueinitialization
(8.5).
- 或仅用于直接初始化和显式转换的转换函数。
C++ [class.conv.fct]
2) A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5). Otherwise, user-defined conversions are not restricted to use in assignments
and initializations.
概述
显式转换函数和构造函数只能用于显式转换(直接初始化或显式转换操作),而非显式构造函数和转换函数可以用于隐式和显式转换。
1 2 3 4 5 6 7 8 9 10 11 12 | /* explicit conversion implicit conversion explicit constructor yes no constructor yes yes explicit conversion function yes no conversion function yes yes */ |
使用结构
让我们看一小部分结构和函数的设置,看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct Z { }; struct X { explicit X(int a); // X can be constructed from int explicitly explicit operator Z (); // X can be converted to Z explicitly }; struct Y{ Y(int a); // int can be implicitly converted to Y operator Z (); // Y can be implicitly converted to Z }; void foo(X x) { } void bar(Y y) { } void baz(Z z) { } |
有关构造函数的示例:
函数参数的转换:
1 2 3 4 5 6 7 | foo(2); // error: no implicit conversion int to X possible foo(X(2)); // OK: direct initialization: explicit conversion foo(static_cast<X>(2)); // OK: explicit conversion bar(2); // OK: implicit conversion via Y(int) bar(Y(2)); // OK: direct initialization bar(static_cast<Y>(2)); // OK: explicit conversion |
对象初始化:
1 2 3 4 5 6 7 8 9 | X x2 = 2; // error: no implicit conversion int to X possible X x3(2); // OK: direct initialization X x4 = X(2); // OK: direct initialization X x5 = static_cast<X>(2); // OK: explicit conversion Y y2 = 2; // OK: implicit conversion via Y(int) Y y3(2); // OK: direct initialization Y y4 = Y(2); // OK: direct initialization Y y5 = static_cast<Y>(2); // OK: explicit conversion |
有关转换函数的示例:
1 2 | X x1{ 0 }; Y y1{ 0 }; |
函数参数的转换:
1 2 3 4 5 6 7 | baz(x1); // error: X not implicitly convertible to Z baz(Z(x1)); // OK: explicit initialization baz(static_cast<Z>(x1)); // OK: explicit conversion baz(y1); // OK: implicit conversion via Y::operator Z() baz(Z(y1)); // OK: direct initialization baz(static_cast<Z>(y1)); // OK: explicit conversion |
对象初始化:
1 2 3 4 5 6 7 8 9 | Z z1 = x1; // error: X not implicitly convertible to Z Z z2(x1); // OK: explicit initialization Z z3 = Z(x1); // OK: explicit initialization Z z4 = static_cast<Z>(x1); // OK: explicit conversion Z z1 = y1; // OK: implicit conversion via Y::operator Z() Z z2(y1); // OK: direct initialization Z z3 = Z(y1); // OK: direct initialization Z z4 = static_cast<Z>(y1); // OK: explicit conversion |
为什么要使用
转换构造函数和非显式转换函数可能会引入歧义。
分别考虑可转换为
1 2 3 4 5 6 7 8 | struct V { operator bool() const { return true; } }; struct U { U(V) { } }; void f(U) { } void f(bool) { } |
如果传递类型为
1 2 | V x; f(x); // error: call of overloaded 'f(V&)' is ambiguous |
编译器不知道是否使用
如果EDOCX1的构造器(8)或EDOCX1的转换函数(6)为
转换构造函数和非显式转换函数可能导致意外的行为。
考虑一个函数打印一些向量:
1 2 | void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << ' '; } |
如果向量的大小构造函数不是显式的,则可以这样调用函数:
1 | print_intvector(3); |
这样的电话会有什么好处?一行包含
正如Bjarne Stroustrup所说的(在C++编程语言中,第四ED.,35.2.1,第1011页)关于为什么EDCOX1〔25〕不能从一个素数隐式构造的问题:
If you know what you mean, be explicit about it.
1 2 3 4 5 6 7 8 9 | class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; } |
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 | class C{ public: explicit inline operator bool(void) const{ return true; } }; int main(void){ C c; bool b = static_cast<bool>(c); return 0; } |
这里,
显式转换构造函数(C++)
The explicit function specifier controls unwanted implicit type
conversions. It can only be used in declarations of constructors
within a class declaration. For example, except for the default
constructor, the constructors in the following class are conversion
constructors.
1 2 3 4 5 6 7 | class A { public: A(); A(int); A(const char*, int = 0); }; |
下列声明是合法的:
1 2 | A c = 1; A d ="Venditti"; |
第一个声明相当于
如果将类的构造函数声明为
例如,如果将类声明为:
1 2 3 4 5 6 7 | class A { public: explicit A(); explicit A(int); explicit A(const char*, int = 0); }; |
只能指定与类类型的值匹配的值。
例如,以下陈述是合法的:
1 2 3 4 5 6 7 | A a1; A a2 = A(1); A a3(1); A a4 = A("Venditti"); A* p = new A(1); A a5 = (A)1; A a6 = static_cast<A>(1); |
CPP参考总是有用的!!!!有关显式说明符的详细信息,请参见此处。您可能还需要查看隐式转换和复制初始化。
快看
The explicit specifier specifies that a constructor or conversion function (since C++11) doesn't allow implicit conversions or copy-initialization.
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | struct A { A(int) { } // converting constructor A(int, int) { } // converting constructor (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK: copy-initialization selects A::A(int) A a2(2); // OK: direct-initialization selects A::A(int) A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int) A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int) A a5 = (A)1; // OK: explicit cast performs static_cast if (a1) cout <<"true" << endl; // OK: A::operator bool() bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization // B b1 = 1; // error: copy-initialization does not consider B::B(int) B b2(2); // OK: direct-initialization selects B::B(int) B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int) // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int) B b5 = (B)1; // OK: explicit cast performs static_cast if (b5) cout <<"true" << endl; // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization } |
这已经讨论过了(什么是显式构造函数)。但我必须说,它缺少这里找到的详细描述。
此外,如前所述,使单参数构造器(包括arg2、arg3等的默认值的构造器)成为一种良好的编码实践。就像C++一样:如果你不这么做,你会希望你…
对于类的另一个好做法是将复制构造和分配设为私有(也就是说,禁用它),除非您确实需要实现它。这避免了在使用默认情况下C++为您创建的方法时指针的最终复制。另一种方法是从boost::noncopyable派生。
构造函数追加隐式转换。要禁止此隐式转换,需要使用参数explicit声明构造函数。
在C++ 11中,还可以指定一个"操作符Type()),用这样的关键字HTTP://E.CPAPYCENCE.COM/W/CPP/语言/显式,使用这种规范,可以使用显式转换的操作符,以及对象的直接初始化。
p.s.当使用由用户定义的转换(通过构造函数和类型转换运算符)时,只允许使用一个级别的隐式转换。但是您可以将这种转换与其他语言转换结合起来
- 向上整数级(char到int,float到double);
- 标准转换(int到double);
- 将对象指针转换为基类和void*;