How do conversion operators work in C++?

半世苍凉 提交于 2019-11-26 05:17:56

问题


Consider this simple example:

template <class Type>
class smartref {
public:
    smartref() : data(new Type) { }
    operator Type&(){ return *data; }
private:
    Type* data;
};

class person {
public:
    void think() { std::cout << \"I am thinking\"; }
};

int main() {
    smartref<person> p;
    p.think(); // why does not the compiler try substituting Type&?
}

How do conversion operators work in C++? (i.e) when does the compiler try substituting the type defined after the conversion operator?


回答1:


Some random situations where conversion functions are used and not used follow.

First, note that conversion functions are never used to convert to the same class type or to a base class type.

Conversion during argument passing

Conversion during argument passing will use the rules for copy initialization. These rules just consider any conversion function, disregarding of whether converting to a reference or not.

struct B { };
struct A {
  operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!

Argument passing is just one context of copy initialization. Another is the "pure" form using the copy initialization syntax

B b = A(); // called!

Conversion to reference

In the conditional operator, conversion to a reference type is possible, if the type converted to is an lvalue.

struct B { };
struct A {
  operator B&() { static B b; return b; }
};

int main() { B b; 0 ? b : A(); } // called!

Another conversion to reference is when you bind a reference, directly

struct B { };
struct A { 
  operator B&() { static B b; return b; }
};

B &b = A(); // called!

Conversion to function pointers

You may have a conversion function to a function pointer or reference, and when a call is made, then it might be used.

typedef void (*fPtr)(int);

void foo(int a);
struct test {
  operator fPtr() { return foo; }
};

int main() {
  test t; t(10); // called!
}

This thing can actually become quite useful sometimes.

Conversion to non class types

The implicit conversions that happen always and everywhere can use user defined conversions too. You may define a conversion function that returns a boolean value

struct test {
  operator bool() { return true; }
};

int main() {
  test t;
  if(t) { ... }
}

(The conversion to bool in this case can be made safer by the safe-bool idiom, to forbid conversions to other integer types.) The conversions are triggered anywhere where a built-in operator expects a certain type. Conversions may get into the way, though.

struct test {
  void operator[](unsigned int) { }
  operator char *() { static char c; return &c; }
};

int main() {
  test t; t[0]; // ambiguous
}

// (t).operator[] (unsigned int) : member
// operator[](T *, std::ptrdiff_t) : built-in

The call can be ambiguous, because for the member, the second parameter needs a conversion, and for the built-in operator, the first needs a user defined conversion. The other two parameters match perfectly respectively. The call can be non-ambiguous in some cases (ptrdiff_t needs be different from int then).

Conversion function template

Templates allow some nice things, but better be very cautious about them. The following makes a type convertible to any pointer type (member pointers aren't seen as "pointer types").

struct test {
  template<typename T>
  operator T*() { return 0; }
};

void *pv = test();
bool *pb = test();



回答2:


The "." operator is not overloadable in C++. And whenever you say x.y, no conversion will automatically be be performed on x.




回答3:


Conversions aren't magic. Just because A has a conversion to B and B has a foo method doesn't mean that a.foo() will call B::foo().

The compiler tries to use a conversion in four situations

  1. You explicitly cast a variable to another type
  2. You pass the variable as an argument to a function that expects a different type in that position (operators count as functions here)
  3. You assign the variable to a variable of a different type
  4. You use the variable copy-construct or initialize a variable of a different type

There are three types of conversions, other than those involved with inheritance

  1. Built-in conversions (e.g. int-to-double)
  2. Implicit construction, where class B defines a constructor taking a single argument of type A, and does not mark it with the "explicit" keyword
  3. User-defined conversion operators, where class A defines an operator B (as in your example)

How the compiler decides which type of conversion to use and when (especially when there are multiple choices) is pretty involved, and I'd do a bad job of trying to condense it into an answer on SO. Section 12.3 of the C++ standard discusses implicit construction and user-defined conversion operators.

(There may be some conversion situations or methods that I haven't thought of, so please comment or edit them if you see something missing)




回答4:


Implicit conversion (whether by conversion operators or non-explicit constructors) occurs when passing parameters to functions (including overloaded and default operators for classes). In addition to this, there are some implicit conversions performed on arithmetic types (so adding a char and a long results in the addition of two longs, with a long result).

Implicit conversion does not apply to the object on which a member function call is made: for the purposes of implicit conversion, "this" is not a function parameter.




回答5:


You should do

((person)p).think();

The compiler doesn't have the information for automatically casting to person, so you need explicit casting.

If you would use something like

person pers = p;

Then the compiler has information for implicit casting to person.

You can have "casting" through constructors:

class A
{
public:
   A( int );
};


A a = 10; // Looks like a cast from int to A

These are some brief examples. Casting (implicit, explicit, etc) needs more to explain. You can find details in serious C++ books (see the questions about C++ books on stack overflow for good titles, like this one).




回答6:


The compiler will attempt one(!) user-defined cast (implicit ctor or cast operator) if you try to use an object (reference) of type T where U is required.

The . operator, however, will always try to access a member of the object (reference) on its left side. That's just the way it's defined. If you want something more fancy, that's what operator->() can be overloaded for.




回答7:


//Virtual table Fuction(VFT)

#include <iostream>

using namespace std;

class smartref {
public:
virtual char think() { }//for Late bindig make virtual function if not make virtual function of char think() {} then become early binding and pointer call this class function 
    smartref() : data(new char) { }
  operator char(){ return *data; }
private:
    char* data;
};

class person:public smartref
{
public:
    char think() { std::cout << "I am thinking"; }
};

int main() {
    smartref *p;//make pointer of class
    person o1;//make object of class
    p=&o1;//store object address in pointer
    p->think(); // Late Binding in class person
return 0;
}


来源:https://stackoverflow.com/questions/1307876/how-do-conversion-operators-work-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!