Ski*_*izz 2753 c++ constructor explicit c++-faq explicit-constructor
explicit关键字在C++中意味着什么?
Ski*_*izz 3165
允许编译器进行一次隐式转换以将参数解析为函数.这意味着编译器可以使用可用单个参数调用的构造函数从一种类型转换为另一种类型,以便为参数获取正确的类型.
这是一个带有构造函数的示例类,可用于隐式转换:
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;
};
Run Code Online (Sandbox Code Playgroud)
这是一个带Foo对象的简单函数:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
Run Code Online (Sandbox Code Playgroud)
这里DoBar是调用函数的地方.
int main ()
{
DoBar (42);
}
Run Code Online (Sandbox Code Playgroud)
这个论点不是一个Foo对象,而是一个int.然而,对于存在一个构造函数Foo,它接受一个int如此此构造可用于将参数转换为正确的类型.
允许编译器为每个参数执行一次此操作.
将explicit关键字前缀到构造函数可防止编译器将该构造函数用于隐式转换.将它添加到上面的类将在函数调用时创建编译器错误DoBar (42).现在有必要明确地调用转换 DoBar (Foo (42))
您可能希望这样做的原因是为了避免可以隐藏错误的意外构造.举例:
MyString(int size)带有构造函数的类,它构造一个给定大小的字符串.你有一个函数print(const MyString&),并调用print(3)(当你真正用于调用print("3")).你希望它打印"3",但它打印一个长度为3的空字符串.小智 1094
假设你有一个班级String:
class String {
public:
String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
Run Code Online (Sandbox Code Playgroud)
现在,如果您尝试:
String mystring = 'x';
Run Code Online (Sandbox Code Playgroud)
该字符'x'将被隐式转换为int,然后String(int)将调用构造函数.但是,这不是用户可能想要的.因此,为了防止这种情况,我们将构造函数定义为explicit:
class String {
public:
explicit String (int n); //allocate n bytes
String(const char *p); // initialize sobject with string p
};
Run Code Online (Sandbox Code Playgroud)
cjm*_*cjm 153
在C++中,只有一个必需参数的构造函数被认为是隐式转换函数.它将参数类型转换为类类型.这是否是好事取决于构造函数的语义.
例如,如果你有一个带有构造函数的字符串类String(const char* s),那可能就是你想要的.您可以将a传递const char*给期望a的函数String,编译器将自动String为您构造一个临时对象.
另一方面,如果你有一个缓冲类,其构造函数Buffer(int size)以字节为单位取缓冲区的大小,你可能不希望编译器悄悄地将ints转换为Buffers.要防止这种情况,请使用explicit关键字声明构造函数:
class Buffer { explicit Buffer(int size); ... }
Run Code Online (Sandbox Code Playgroud)
那样,
void useBuffer(Buffer& buf);
useBuffer(4);
Run Code Online (Sandbox Code Playgroud)
成为编译时错误.如果要传递临时Buffer对象,则必须明确地执行此操作:
useBuffer(Buffer(4));
Run Code Online (Sandbox Code Playgroud)
总之,如果您的单参数构造函数将参数转换为类的对象,您可能不希望使用该explicit关键字.但是如果你有一个构造函数只是碰巧采用一个参数,你应该声明它explicit以防止编译器意外转换让你感到惊讶.
cod*_*nza 43
显式转换构造函数(仅限C++)
显式函数说明符控制不需要的隐式类型转换.它只能用于类声明中的构造函数声明.例如,除了默认构造函数之外,以下类中的构造函数是转换构造函数.
class A
{
public:
A();
A(int);
A(const char*, int = 0);
};
Run Code Online (Sandbox Code Playgroud)
以下声明是合法的:
A c = 1;
A d = "Venditti";
Run Code Online (Sandbox Code Playgroud)
第一个声明相当于A c = A( 1 );.
如果将类的构造函数声明为explicit,则先前的声明将是非法的.
例如,如果您将类声明为:
class A
{
public:
explicit A();
explicit A(int);
explicit A(const char*, int = 0);
};
Run Code Online (Sandbox Code Playgroud)
您只能分配与类类型值匹配的值.
例如,以下陈述是合法的:
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);
Run Code Online (Sandbox Code Playgroud)
Gau*_*tam 41
这个答案是关于有/没有显式构造函数的对象创建,因为它没有在其他答案中涵盖.
考虑以下没有显式构造函数的类:
class Foo
{
public:
Foo(int x) : m_x(x)
{
}
private:
int m_x;
};
Run Code Online (Sandbox Code Playgroud)
类Foo的对象可以通过两种方式创建:
Foo bar1(10);
Foo bar2 = 20;
Run Code Online (Sandbox Code Playgroud)
根据实现,实例化类Foo的第二种方式可能令人困惑,或者不是程序员想要的.将explicit关键字前缀到构造函数会在以下位置生成编译器错误Foo bar2 = 20;.
将单参数构造函数声明为通常是一种好习惯explicit,除非您的实现明确禁止它.
还要注意构造函数
都可以用作单参数构造函数.所以你可能也想做这些explicit.
当你会故意的例子并不想使你的单参数的构造函数明确的是,如果你要创建一个仿函数(看"ADD_X"在结构中声明这个答案).在这种情况下,创建一个add_x add30 = 30;可能有意义的对象.
这是对显式构造函数的一个很好的写作.
Pix*_*ist 39
explicit伴随着C++ [class.conv.ctor]
1)在没有函数说明符explicit的情况下声明的构造函数指定从其参数类型到其类类型的转换.这样的构造函数称为转换构造函数.
2)显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法(8.5)或强制转换(5.2.9,5.4)的情况下才这样做.默认构造函数可以是显式构造函数; 这样的构造函数将用于执行默认初始化或valueinitialization(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
*/
Run Code Online (Sandbox Code Playgroud)
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) { }
Run Code Online (Sandbox Code Playgroud)
转换函数参数:
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
Run Code Online (Sandbox Code Playgroud)
对象初始化:
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
Run Code Online (Sandbox Code Playgroud)
X x1{ 0 };
Y y1{ 0 };
Run Code Online (Sandbox Code Playgroud)
转换函数参数:
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
Run Code Online (Sandbox Code Playgroud)
对象初始化:
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
Run Code Online (Sandbox Code Playgroud)
explicit转换函数或构造函数?转换构造函数和非显式转换函数可能引入歧义.
考虑一种结构V,可转换到int,一个结构U从隐式constructible V以及功能f重载U和bool分别.
struct V {
operator bool() const { return true; }
};
struct U { U(V) { } };
void f(U) { }
void f(bool) { }
Run Code Online (Sandbox Code Playgroud)
f如果传递类型的对象,则调用是不明确的V.
V x;
f(x); // error: call of overloaded 'f(V&)' is ambiguous
Run Code Online (Sandbox Code Playgroud)
编译器不知道使用构造函数U或转换函数将V对象转换为传递类型f.
如果是在建构U或转换功能V是explicit,将不会有歧义,因为只有非显式转换将被考虑.如果两者都是显式的,则必须使用显式转换或强制转换操作来完成对使用f类型对象的调用V.
转换构造函数和非显式转换函数可能会导致意外行为.
考虑打印一些向量的函数:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
Run Code Online (Sandbox Code Playgroud)
如果向量的size-constructor不是显式的,则可以像这样调用函数:
print_intvector(3);
Run Code Online (Sandbox Code Playgroud)
人们对这样的电话有什么期望?一行包含3三行包含0?(第二个是发生的事情.)
正如Bjarne Stroustrup所述(在"The C++ Programming Language",第4版,35.2.1,第1011页)中提出的问题为什么std::duration不能用普通数字隐式构造:
如果您知道自己的意思,请明确说明.
Hel*_*irr 27
该explicit-keyword可以用来强制执行构造函数被调用明确.
class C{
public:
explicit C(void) = default;
};
int main(void){
C c();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
explicit构造函数前面的-keyword C(void)告诉编译器只允许显式调用此构造函数.
所述explicit-keyword也可以在用户定义类型转换操作者使用:
class C{
public:
explicit inline operator bool(void) const{
return true;
}
};
int main(void){
C c;
bool b = static_cast<bool>(c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里,explicit-keyword只强制显式强制转换为有效,因此bool b = c;在这种情况下是无效的强制转换.在这样的情况下explicit-keyword可以帮助程序员避免隐式的,非预期的强制转换.这种用法已在C++ 11中标准化.
sel*_*oot 20
Cpp参考总是很有用!!! 有关显式说明符的详细信息,请参见此处.您可能还需要查看隐式转换和复制初始化.
快速浏览
显式说明符指定构造函数或转换函数(自C++ 11以来)不允许隐式转换或复制初始化.
示例如下:
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
}
Run Code Online (Sandbox Code Playgroud)
构造函数附加隐式转换。为了禁止这种隐式转换,需要声明一个带有显式参数的构造函数。
在C ++ 11中,您还可以使用关键字http://en.cppreference.com/w/cpp/language/explicit指定“ operator type()”,通过这种规范,您可以在显式转换方面使用operator,并且直接初始化对象。
PS当使用由USER定义的转换(通过构造函数和类型转换运算符)时,仅允许使用一级隐式转换。但您可以将此转换与其他语言转换结合使用
| 归档时间: |
|
| 查看次数: |
808647 次 |
| 最近记录: |