允许编译器进行一次隐式转换以将参数解析为函数 . 这意味着编译器可以使用可用 single parameter 调用的构造函数将一种类型转换为另一种类型,以便为参数获取正确的类型 .
这是一个带有构造函数的示例类,可用于隐式转换:
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;
};
这是一个带有 Foo 对象的简单函数:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
这里是调用 DoBar 函数的地方 .
int main ()
{
DoBar (42);
}
参数不是 Foo 对象,而是 int . 但是, Foo 的构造函数需要 int ,因此可以使用此构造函数将参数转换为正确的类型 .
允许编译器为每个参数执行一次此操作 .
将 explicit 关键字前缀到构造函数可防止编译器将该构造函数用于隐式转换 . 将它添加到上面的类将在函数调用 DoBar (42) 处创建编译器错误 . 现在需要使用 DoBar (Foo (42)) 明确调用转换
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
为什么要使用显式转换函数或构造函数?
Conversion constructors and non-explicit conversion functions may introduce ambiguity.
考虑一个结构 V ,可转换为 int ,一个结构 U 可以从 V 隐式构造,一个函数 f 分别为 U 和 bool 重载 .
11 回答
允许编译器进行一次隐式转换以将参数解析为函数 . 这意味着编译器可以使用可用 single parameter 调用的构造函数将一种类型转换为另一种类型,以便为参数获取正确的类型 .
这是一个带有构造函数的示例类,可用于隐式转换:
这是一个带有
Foo
对象的简单函数:这里是调用
DoBar
函数的地方 .参数不是
Foo
对象,而是int
. 但是,Foo
的构造函数需要int
,因此可以使用此构造函数将参数转换为正确的类型 .允许编译器为每个参数执行一次此操作 .
将
explicit
关键字前缀到构造函数可防止编译器将该构造函数用于隐式转换 . 将它添加到上面的类将在函数调用DoBar (42)
处创建编译器错误 . 现在需要使用DoBar (Foo (42))
明确调用转换您可能希望这样做的原因是为了避免可以隐藏错误的意外构造 . 举例:
MyString(int size)
类,该构造函数构造给定大小的字符串 . 你有一个函数print(const MyString&)
,你调用print(3)
(当你打算调用print("3")
时) . 您希望它打印"3",但它会打印一个长度为3的空字符串 .假设你有一个类
String
:现在,如果你尝试:
字符
'x'
将隐式转换为int
,然后将调用String(int)
构造函数 . 但是,这不是用户可能想要的 . 因此,为了防止这种情况,我们将构造函数定义为explicit
:在C中,只有一个必需参数的构造函数被认为是隐式转换函数 . 它将参数类型转换为类类型 . 这是否是好事取决于构造函数的语义 .
例如,如果你有一个带有构造函数
String(const char* s)
的字符串类,那可能就是你想要的 . 您可以将const char*
传递给期望String
的函数,编译器将自动为您构造临时String
对象 .另一方面,如果你有一个缓冲类,其构造函数
Buffer(int size)
以字节为单位取缓冲区的大小,你可能不希望编译器悄悄地将int
s转换为Buffer
. 要防止这种情况,请使用explicit
关键字声明构造函数:那样,
成为编译时错误 . 如果要传递临时
Buffer
对象,则必须明确地执行此操作:总之,如果您的单参数构造函数将参数转换为类的对象,您可能不希望使用
explicit
关键字 . 但是如果你的构造函数恰好采用了一个参数,那么你应该将它声明为explicit
,以防止编译器因意外的转换而让你感到惊讶 .这个答案是关于有/没有显式构造函数的对象创建,因为它没有在其他答案中涵盖 .
考虑以下没有显式构造函数的类:
类Foo的对象可以通过两种方式创建:
根据实现,实例化类Foo的第二种方式可能令人困惑,或者不是程序员想要的 . 将
explicit
关键字前缀到构造函数会在Foo bar2 = 20;
生成编译器错误 .将单参数构造函数声明为
explicit
通常是一种好习惯,除非您的实现明确禁止它 .还要注意构造函数
所有参数的默认参数,或
第二个参数的默认参数
都可以用作单参数构造函数 . 所以你可能想要这些也是
explicit
.例如,当你故意让你的单参数构造函数显式时,如果你在this中声明了're creating a functor (look at the ' add_x'结构 . 在这种情况下,将对象创建为
add_x add30 = 30;
可能是有意义的 .Here是对显式构造函数的一个很好的写作 .
explicit
关键字将转换构造函数转换为非转换构造函数 . 因此,代码不易出错 .关键字explicit伴随着
概述
显式转换函数和构造函数只能用于显式转换(直接初始化或显式转换操作),而非显式构造函数和转换函数可用于隐式转换和显式转换 .
使用结构X,Y,Z和函数foo,bar,baz的示例:
让我们看一下结构和函数的小型设置,以查看
explicit
和非explicit
转换之间的区别 .关于构造函数的示例:
转换函数参数:
对象初始化:
关于转换函数的示例:
转换函数参数:
对象初始化:
为什么要使用显式转换函数或构造函数?
Conversion constructors and non-explicit conversion functions may introduce ambiguity.
考虑一个结构
V
,可转换为int
,一个结构U
可以从V
隐式构造,一个函数f
分别为U
和bool
重载 .如果传递
V
类型的对象,则对f
的调用不明确 .编译器不知道是否使用
U
的构造函数或转换函数将V
对象转换为传递给f
的类型 .如果
U
的构造函数或V
的转换函数是explicit
,则不会产生歧义,因为只考虑非显式转换 . 如果两者都是显式的,则必须使用显式转换或强制转换操作来使用V
类型的对象调用f
.Conversion constructors and non-explicit conversion functions may lead to unexpected behaviour.
考虑打印一些向量的函数:
如果向量的size-constructor不是显式的,则可以像这样调用函数:
人们对这样的电话会有什么期望?一行包含
3
或三行包含0
? (第二个是发生的事情 . )在类接口中使用explicit关键字可强制接口的用户明确说明所需的转换 .
正如Bjarne Stroustrup所述(在"The C++ Programming Language",第4版,35.2.1,第1011页)中提出的问题为什么
std::duration
不能用普通数字隐式构造:explicit
-keyword可用于强制显式调用构造函数 .构造函数
C(void)
前面的explicit
-keyword告诉编译器只允许显式调用此构造函数 .explicit
-keyword也可以在用户定义的类型转换运算符中使用:这里,
explicit
-keyword仅强制显式强制转换为有效,因此在这种情况下bool b = c;
将是无效的强制转换 . 在像这样的情况下explicit
-keyword可以帮助程序员避免隐式的,非预期的强制转换 . 此用法已在C++11中标准化 .这已经讨论过(what is explicit constructor) . 但我必须说,它缺乏这里的详细描述 .
此外,如上所述,使用一个参数构造函数(包括那些具有arg2,arg3,...的默认值的构造函数)总是一个很好的编码实践 . 和C一样:如果你不这样做 - 你会希望你做到......
类的另一个好习惯是将复制构造和赋值设置为私有(a.k.a . 禁用它),除非你真的需要实现它 . 这样可以避免在使用C默认为您创建的方法时最终获得指针副本 . 另一种方法是从boost :: noncopyable派生 .
Cpp参考总是很有用!!!有关显式说明符的详细信息可以在here找到 . 您可能还需要查看implicit conversions和copy-initialization .
快速浏览
示例如下:
Explicit conversion constructors (C++ only)
以下声明是合法的:
第一个声明相当于
A c = A( 1 );
.如果将类的构造函数声明为
explicit
,则先前的声明将是非法的 .例如,如果您将类声明为:
您只能分配值匹配类类型的值 .
例如,以下陈述是合法的:
构造函数附加隐式转换 . 要禁止此隐式转换,需要声明带有显式参数的构造函数 .
在C 11中,您还可以使用此类关键字指定"operator type()" http://en.cppreference.com/w/cpp/language/explicit使用此类规范,您可以在显式转换和对象的直接初始化方面使用运算符 .
附:当使用BY USER定义的转换(通过构造函数和类型转换运算符)时,只允许使用一级隐式转换 . 但您可以将此转化与其他语言转换结合使用
up积分排名(char为int,float为double);
标准转换(int to double);
将对象的指针转换为基类和void *;