46

考虑这个简单的例子:

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&?
}

转换运算符如何在 C++ 中工作?(即)编译器何时尝试替换转换运算符之后定义的类型?

4

7 回答 7

63

以下是一些使用和不使用转换函数的随机情况。

首先,请注意转换函数永远不会用于转换为相同的类类型或基类类型。

参数传递期间的转换

参数传递期间的转换将使用复制初始化的规则。这些规则只考虑任何转换函数,不管是否转换为引用。

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

参数传递只是复制初始化的一种上下文。另一种是使用复制初始化语法的“纯”形式

B b = A(); // called!

转换为参考

在条件运算符中,如果转换为左值的类型,则可以转换为引用类型。

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

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

另一种到引用的转换是当您直接绑定引用时

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

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

转换为函数指针

您可能有一个到函数指针或引用的转换函数,并且当进行调用时,它可能会被使用。

typedef void (*fPtr)(int);

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

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

这个东西有时实际上会变得非常有用。

转换为非类类型

随时随地发生的隐式转换也可以使用用户定义的转换。您可以定义一个返回布尔值的转换函数

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

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

(在这种情况下,转换为 bool 可以通过safe-bool 习惯用法变得更安全,以禁止转换为其他整数类型。)在内置运算符需要某种类型的任何地方都会触发转换。不过,转换可能会妨碍您。

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

调用可能不明确,因为对于成员,第二个参数需要转换,而对于内置运算符,第一个参数需要用户定义的转换。其他两个参数分别完美匹配。在某些情况下,调用可以是明确的(ptrdiff_t需要与int那时不同)。

转换函数模板

模板允许一些不错的东西,但最好对它们非常谨慎。以下使类型可转换为任何指针类型(成员指针不被视为“指针类型”)。

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

void *pv = test();
bool *pb = test();
于 2009-08-20T19:31:18.663 回答
17

这 ”。” 运算符在 C++ 中不可重载。并且每当您说 xy 时,不会自动对 x 执行转换。

于 2009-08-20T18:24:43.727 回答
10

转换不是魔术。仅仅因为 A 有一个到 B 的转换并且 B 有一个 foo 方法并不意味着 a.foo() 会调用 B::foo()。

编译器尝试在四种情况下使用转换

  1. 您将变量显式转换为另一种类型
  2. 您将变量作为参数传递给在该位置需要不同类型的函数(操作符在这里算作函数)
  3. 您将变量分配给不同类型的变量
  4. 您使用变量复制构造或初始化不同类型的变量

除了与继承有关的转换外,还有三种类型的转换

  1. 内置转换(例如 int 到 double)
  2. 隐式构造,其中 B 类定义了一个采用 A 类型的单个参数的构造函数,并且没有用“显式”关键字标记它
  3. 用户定义的转换运算符,其中 A 类定义了运算符 B(如您的示例中所示)

编译器如何决定使用哪种类型的转换以及何时(特别是当有多种选择时)非常复杂,而且我在尝试将其浓缩为关于 SO 的答案方面做得很糟糕。C++ 标准的第 12.3 节讨论了隐式构造和用户定义的转换运算符。

(可能有一些我没有想到的转换情况或方法,所以如果你发现有什么遗漏的地方请评论或编辑)

于 2009-08-20T18:22:27.290 回答
7

将参数传递给函数(包括类的重载和默认运算符)时会发生隐式转换(无论是通过转换运算符还是非显式构造函数)。除此之外,还有一些对算术类型执行的隐式转换(因此添加一个 char 和一个 long 会导致两个 long 相加,得到一个 long 结果)。

隐式转换不适用于对其进行成员函数调用的对象:出于隐式转换的目的,“this”不是函数参数。

于 2009-08-20T18:19:58.137 回答
1

如果您尝试在需要的T地方使用类型的对象(引用),编译器将尝试一个(!)用户定义的转换(隐式 ctor 或转换运算符) U

但是,.操作员将始终尝试访问其左侧的对象(引用)的成员。这就是它的定义方式。如果您想要更花哨的东西,那就operator->()可以重载。

于 2009-08-20T18:19:04.473 回答
0

你应该做

((person)p).think();

编译器没有自动转换为人员的信息,因此您需要显式转换。

如果你会使用类似的东西

person pers = p;

然后编译器具有隐式转换为人员的信息。

您可以通过构造函数进行“强制转换”:

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


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

这些是一些简短的例子。转换(隐式、显式等)需要更多解释。您可以在严肃的 C++ 书籍中找到详细信息(请参阅关于堆栈溢出的 C++ 书籍的问题,以获得好的标题,例如这个)。

于 2009-08-20T18:14:58.677 回答
-1

//虚拟表函数(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;
}
于 2017-09-14T10:06:17.770 回答