显式关键字应用于运算符而不是构造函数

时间:2018-09-24 15:47:37

标签: c++ c++11 operator-keyword explicit

在下面的课程中,

您为什么要使运算符为explicit。我以为explicit是为了防止隐式调用构造函数?

 class Content
            {
public:

 virtual ~Content() = 0;
 virtual explicit operator float&();
 virtual explicit operator long long&();
 virtual explicit operator std::string&()
}

4 个答案:

答案 0 :(得分:3)

  

我认为显式是为了防止隐式调用   构造函数?

自C ++ 11起,它也适用于user-defined conversions(又称​​ cast运算符)。

  

为什么要让运营商成为explicit

在此上下文中使用的explicit关键字使转换仅适用于直接初始化显式转换。请参见[class.conv.fct¶2]下的此处:

  

在这种情况下,转换函数可能是显式的([dcl.fct.spec])   仅被视为用户定义的转化   直接初始化([dcl.init])。否则,用户定义   转换不限于用于作业和   初始化。

这可以帮助您确保编译器不会尝试违反您的意图进行转换,因此您必须自己明确地进行转换,从而留出更少的错误空间。示例:

struct Foo
{
    explicit operator int() {return 0;}
    operator int*() {return nullptr;}
};

int main()
{
    Foo foo;

    //int xi = foo; // Error, conversion must be explicit
    int i = static_cast<int>(foo); // OK, conversion is explicit
    int* i_ptr = foo; // OK, implicit conversion to `int*` is allowed

    int i_direct(foo); // OK, direct initialization allowed
    int* i_ptr_direct(foo); // OK, direct initialization after implicit conversion

    return 0;
}

在应用多个转换选项的情况下,它还可以帮助解决歧义,使编译器没有选择哪个选项的标准:

struct Bar
{
    operator int() {return 1;}
    operator char() {return '1';}
};

int main()
{
    Bar bar;    
    //double d = bar; // Error, implicit conversion is ambiguous    
    return 0;
}

添加explicit

struct Bar
{
    operator int() {return 1;}
    explicit operator char() {return '1';}
};

int main()
{
    Bar bar;    
    double d = bar; // OK, implicit conversion to `int` is the only option    
    return 0;
}

答案 1 :(得分:2)

请考虑以下内容:

struct Content
{
    operator float() { return 42.f; }  
    friend Content operator+(Content& lhs, float) { return lhs; }
};

int main()
{
    Content c{};
    c + 0; //  error: ambiguous overload for 'operator+'
}

在这里,编译器无法在operator+(Content&, float)operator+(float, int)之间进行选择。使浮点运算符explicit解决了这种歧义*:

c + 0; // operator+(Content&, float)

static_cast<float>(c) + 0; // operator+(float, int)

*),只要优先选择一个即可。

答案 2 :(得分:2)

其他答案涵盖了它的工作原理,但是我认为应该告诉您为什么将它添加到C ++中。

智能指针通常可以转换为bool,因此您可以执行以下操作:

std::shared_ptr<int> foo;
if (foo) {
  *foo = 7;
}

其中if(foo)foo转换为bool。不幸的是:

int x = foo+2;

foo转换为bool,然后转换为int,然后添加2。这几乎总是一个错误。之所以允许这样做,是因为虽然只完成了一个用户定义的转换,但是在用户定义的转换之后进行的内置转换却可以静默进行。

要解决此问题,程序员会做疯狂的事情,例如添加:

struct secret {
  void unused();
};
struct smart_ptr {
  using secret_mem_ptr = void(secret::*)();
  operator secret_mem_ptr() const { return 0; }
};

secret_mem_ptr是指向成员的秘密指针。指向成员的指针具有到bool的内置转换,因此:

smart_ptr ptr;
if (!ptr) {
}

“有效”-ptr转换为secret_mem_ptr,然后将其转换为bool,然后用于决定采用哪个分支。

这不仅仅是一个hack。

他们在转换运算符上添加了explicit,以解决这个确切的问题。

现在:

struct smart_ptr {
  explicit operator bool() const { return true; }
};

不允许:

smart_ptr ptr;
int x = 3 + ptr;

但它确实允许:

if (ptr) {
}

因为这些规则是手工制定的,以完全支持该用例。它还不允许:

bool test() {
  smart_ptr ptr;
  return ptr;
}

在这里,您必须输入:

bool test() {
  smart_ptr ptr;
  return (bool)ptr;
}

您将ptr显式转换为bool的地方。

(我通常真的反对C风格的强制转换;在(bool)的情况下我是一个例外)。

答案 3 :(得分:1)

如果希望不要将Content对象隐式转换为(例如)float,则可以使用它。这可以通过以下方式发生:

 void f( float f );
 ....
 Content c;
 f( c );      // conversion from Content to float

如果没有explicit限定词,则转换将隐式进行;使用它,您会得到一个编译错误。

无声的,隐式的转换可能会引起许多混乱和/或错误,因此通常最好使运算符显式,或者可能最好提供诸如ToFloat这样的命名函数,以告知操作者读者究竟是怎么回事。