显式关键字在C++中意味着什么?
当前回答
使单参数构造函数(包括具有arg2、arg3、…的默认值的构造函数)如前所述始终是一种良好的编码实践。就像C++一样:如果你不这样做,你会希望你这样做。。。
类的另一个好做法是将副本构造和赋值设为私有(也就是禁用它),除非你真的需要实现它。这避免了在使用C++默认为你创建的方法时,指针的最终副本。另一种方法是从boost::noncopyable派生。
其他回答
关键字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的示例:
让我们看一个小的结构和函数设置,看看显式转换和非显式转换之间的区别。
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
为什么使用显式转换函数或构造函数?
转换构造函数和非显式转换函数可能会引入歧义。
考虑一个可转换为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的转换函数是显式的,则不会有歧义,因为只考虑非显式转换。如果两者都是显式的,则必须使用显式转换或强制转换操作来使用类型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(在“C++编程语言”,第4版,35.2.1,第1011页)所说的,为什么std::duration不能从一个简单的数字隐式构建:
如果你知道你的意思,就明确地说出来。
构造函数附加隐式转换。若要抑制此隐式转换,需要使用显式参数声明构造函数。
在C++11中,您还可以使用这样的关键字指定“operator type()”http://en.cppreference.com/w/cpp/language/explicit使用这种规范,您可以在显式转换和直接初始化对象方面使用运算符。
P.S.当使用USER定义的转换(通过构造函数和类型转换运算符)时,只允许使用一级隐式转换。但您可以将这种转换与其他语言转换结合起来
向上整数列(char到int,float到double);标准转换(int到double);将对象指针转换为基类和void*;
这个答案是关于带/不带显式构造函数的对象创建的,因为其他答案中没有涉及它。
考虑以下没有显式构造函数的类:
class Foo
{
public:
Foo(int x) : m_x(x)
{
}
private:
int m_x;
};
Foo类的对象可以通过两种方式创建:
Foo bar1(10);
Foo bar2 = 20;
根据实现的不同,实例化类Foo的第二种方式可能会令人困惑,或者不是程序员想要的。将显式关键字前缀到构造函数将在Foobar2=20;处生成编译器错误;。
通常最好将单参数构造函数声明为显式的,除非您的实现明确禁止。
还请注意,具有
所有参数的默认参数,或第二个参数的默认参数
都可以用作单参数构造函数。因此,您可能希望将这些也明确化。
例如,如果您正在创建一个函子(请查看此答案中声明的“add_x”结构),那么您可能会故意不想使单参数构造函数显式。在这种情况下,创建add_x add30=30的对象;可能会有意义。
这里有一篇关于显式构造函数的好文章。
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
}
显式关键字可用于强制显式调用构造函数。
class C {
public:
explicit C() =default;
};
int main() {
C c;
return 0;
}
构造函数C()前面的显式关键字告诉编译器只允许显式调用此构造函数。
显式关键字也可以在用户定义的类型转换运算符中使用:
class C{
public:
explicit inline operator bool() const {
return true;
}
};
int main() {
C c;
bool b = static_cast<bool>(c);
return 0;
}
这里,显式关键字只强制显式强制转换为有效,因此bool b=c;在这种情况下将是无效的强制转换。在类似于这些显式关键字的情况下,可以帮助程序员避免隐式、非预期的强制转换。这种用法已在C++11中标准化。
推荐文章
- 为什么这个结合赋值和相等检查的if语句返回true?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c++枚举类可以有方法吗?
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?
- main()中的Return语句vs exit()
- 为什么c#不提供c++风格的'friend'关键字?
- 在函数的签名中添加关键字
- 我可以调用重载构造函数从另一个构造函数的同类在c# ?
- 我如何在Visual Studio中预处理后看到C/ c++源文件?
- 为什么在标准容器中使用std::auto_ptr<>是错误的?
- 用比较double和0
- 保护可执行文件不受逆向工程的影响?
- 在c++中字符串前面的“L”是什么意思?