为什么显式允许具有2个或更多(非默认)参数的默认构造函数和构造函数?

时间:2010-12-17 02:17:44

标签: c++ explicit-constructor

我理解具有一个(非默认)参数的构造函数就像隐式转换器,它从该参数类型转换为类类型。但是,explicit可用于限定任何构造函数,没有参数的构造函数(默认构造函数)或具有2个或更多(非默认)参数的构造函数。

为什么在这些构造函数上允许显式?是否有任何示例可以防止某种类型的隐式转换?

5 个答案:

答案 0 :(得分:37)

一个原因当然是因为它没有受到伤害。

如果你有第一个参数的默认参数,那么需要它的一个原因是。构造函数成为默认构造函数,但仍可用作转换构造函数

struct A {
  explicit A(int = 0); // added it to a default constructor
};

C ++ 0x实际上将它用于多参数构造函数。在C ++ 0x中,可以使用初始化列表来初始化类对象。哲学是

  • 如果使用= { ... },则使用一种“复合值”初始化对象,该复合值在概念上表示对象的抽象值,并且您希望转换为该类型。 / p>

  • 如果您使用{ ... }初始化程序,则直接调用该对象的构造函数,而不一定要指定转换。

考虑这个例子

struct String {
    // this is a non-converting constructor
    explicit String(int initialLength, int capacity);
};

struct Address {
    // converting constructor
    Address(string name, string street, string city);
};

String s = { 10, 15 }; // error!
String s1{10, 15}; // fine

Address a = { "litb", "nerdsway", "frankfurt" }; // fine

通过这种方式,C ++ 0x表明C ++ 03的决定,允许在其他构造函数上显式,根本不是一个坏主意。

答案 1 :(得分:7)

这可能只是一种便利;没有理由 dis - 允许它,为什么要让代码生成器等生活变得困难?如果您选中了,那么代码生成例程必须有一个额外的步骤来验证生成的构造函数有多少参数。

根据various sources,当应用于无法使用一个参数调用的构造函数时,它根本没有任何效果。

答案 2 :(得分:7)

也许是为了支持维护。通过在多参数构造函数上使用explicit,可以避免在向参数添加默认值时无意中引入隐式转换。虽然我不相信;相反,我认为C ++中允许的很多东西只是为了不使语言定义比现在更复杂。

也许最臭名昭着的案例是返回对非static局部变量的引用。它需要额外的复杂规则来排除所有“无意义”的事情,而不会影响其他任何事情。所以它只是允许,如果你使用那个引用就会产生UB。

或者对于构造函数,只要它们的签名不同,就可以定义任意数量的默认构造函数,但是如果有多个默认构造函数,则默认情况下调用它们是相当困难的。 : - )

或许更好的问题是,为什么转换运算符上也不允许explicit

好吧,在C ++ 0x中。所以没有充分理由不这样做。不允许转换操作符explicit的实际原因可能是疏忽,或者首先采用explicit,或者简单地确定委员会时间的优先级等等。

干杯&第h。,

答案 3 :(得分:5)

根据高完整性C ++编码标准 ,您应该将所有sinlge参数构造函数声明为 显式 ,以避免偶然使用类型转换。在它是一个多参数构造函数的情况下,假设你有一个接受多个参数的构造函数,每个参数都有一个默认值,在某种默认构造函数和转换构造函数中转换构造函数:

class C { 
    public: 
    C( const C& );   // ok copy 
    constructor C(); // ok default constructor 
    C( int, int ); // ok more than one non-default argument 

    explicit C( int ); // prefer 
    C( double ); // avoid 
    C( float f, int i=0 ); // avoid, implicit conversion constructor 
    C( int i=0, float f=0.0 ); // avoid, default constructor, but 
                               // also a conversion constructor 
}; 
void bar( C const & ); 
void foo() 
{ 
    bar( 10 );  // compile error must be 'bar( C( 10 ) )' 
    bar( 0.0 ); // implicit conversion to C 
}

答案 4 :(得分:0)

显式默认构造函数的一个原因是,当class_t::operator=的重载接受类型为U的对象时,避免在赋值右侧易于出错的隐式转换。 std::is_same_v<U, class_t> == false。如果class_t_instance = {}这样的赋值会导致我们产生不希望的结果,例如,如果我们有一个observable<T>会将移动赋值运算符重载到observable<T>::operator=(U&&)之类,而U应该可转换为T。可以使用一个默认构造的T(观察到的类型对象)的分配来编写令人困惑的分配,但是实际上程序员正在“擦除” observable<T>,因为该分配与{{ 1}}(如果默认构造函数是隐式的)。看一下class_t_instance = class_t_instance{}的玩具实现:

observable<T>