关键字explicit 伴随着任一
- 类 X 的构造函数,不能用于将第一个(仅任何一个)参数隐式转换为 X 类型
C++ [class.conv.ctor]
1) 没有函数说明符显式声明的构造函数指定从其参数类型到其类的类型的转换。这样的构造函数称为转换构造函数。
2) 显式构造函数像非显式构造函数一样构造对象,但仅在显式使用直接初始化语法 (8.5) 或强制转换 (5.2.9, 5.4) 的情况下这样做。默认构造函数可以是显式构造函数;这样的构造函数将用于执行默认初始化或值初始化
(8.5)。
C++ [class.conv.fct]
2) 转换函数可能是显式的 (7.1.2),在这种情况下,它仅被视为用于直接初始化 (8.5) 的用户定义转换。否则,用户定义的转换不限于在分配中使用
和初始化。
概述
显式转换函数和构造函数只能用于显式转换(直接初始化或显式转换操作),而非显式构造函数和转换函数可用于隐式和显式转换。
/*
explicit conversion implicit conversion
explicit constructor yes no
constructor yes yes
explicit conversion function yes no
conversion function yes yes
*/
使用结构X, Y, Z 和函数foo, bar, baz 的示例:
让我们看一下结构和函数的小设置,看看explicit 和非explicit 转换之间的区别。
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) { }
关于构造函数的例子:
函数参数的转换:
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
对象初始化:
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
关于转换函数的例子:
X x1{ 0 };
Y y1{ 0 };
函数参数的转换:
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
对象初始化:
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
为什么要使用explicit 转换函数或构造函数?
转换构造函数和非显式转换函数可能会引入歧义。
考虑一个可转换为int 的结构V,一个可从V 隐式构造的结构U,以及一个分别为U 和bool 重载的函数f。
struct V {
operator bool() const { return true; }
};
struct U { U(V) { } };
void f(U) { }
void f(bool) { }
如果传递V 类型的对象,则对f 的调用不明确。
V x;
f(x); // error: call of overloaded 'f(V&)' is ambiguous
编译器不知道是使用U的构造函数还是使用转换函数将V对象转换为传递给f的类型。
如果U 的构造函数或V 的转换函数为explicit,则不会有歧义,因为只会考虑非显式转换。如果两者都是显式的,则使用 V 类型的对象对 f 的调用必须使用显式转换或强制转换操作来完成。
转换构造函数和非显式转换函数可能会导致意外行为。
考虑一个打印一些向量的函数:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
如果向量的大小构造函数不明确,则可以像这样调用函数:
print_intvector(3);
人们对这样的电话有何期待?一行包含3 还是三行包含0? (第二个是发生了什么。)
在类接口中使用显式关键字强制接口的用户明确说明所需的转换。
正如 Bjarne Stroustrup 所说(在“The C++ Programming Language”,第 4 版,35.2.1,第 1011 页)关于为什么不能从普通数字隐式构造 std::duration 的问题:
如果您知道自己的意思,请明确说明。