转换运算符如何在C++中工作?

Ara*_*raK 38 c++ conversion-operator

考虑这个简单的例子:

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&?
}
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*itb 53

使用转换函数但未使用转换函数的一些随机情况如下.

首先,请注意转换函数从不用于转换为相同的类类型或基类类型.

参数传递期间的转换

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

struct B { };
struct A {
  operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!
Run Code Online (Sandbox Code Playgroud)

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

B b = A(); // called!
Run Code Online (Sandbox Code Playgroud)

转换为参考

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

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

int main() { B b; 0 ? b : A(); } // called!
Run Code Online (Sandbox Code Playgroud)

另一个转换为引用是指直接绑定引用

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

B &b = A(); // called!
Run Code Online (Sandbox Code Playgroud)

转换为函数指针

您可能具有函数指针或引用的转换函数,并且在进行调用时,可能会使用它.

typedef void (*fPtr)(int);

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

int main() {
  test t; t(10); // called!
}
Run Code Online (Sandbox Code Playgroud)

这个东西有时候会变得非常有用.

转换为非类型

始终和任何地方发生的隐式转换也可以使用用户定义的转换.您可以定义一个返回布尔值的转换函数

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

int main() {
  test t;
  if(t) { ... }
}
Run Code Online (Sandbox Code Playgroud)

(在这种情况下,转换为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
Run Code Online (Sandbox Code Playgroud)

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

转换功能模板

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

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

void *pv = test();
bool *pb = test();
Run Code Online (Sandbox Code Playgroud)

  • 实际上,您可能会注意到litb的代码没有使用".".运营商.所以我不认为它解决了你原来的问题. (2认同)

小智 16

"." 运算符在C++中不可重载.无论何时说xy,都不会自动对x执行转换.

  • +'表示'.'的事实 不能超载. (4认同)

Tyl*_*nry 9

转换不是魔术.仅仅因为A转换为B而B具有foo方法并不意味着a.foo()将调用B :: foo().

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

  1. 您明确地将变量转换为另一种类型
  2. 您将变量作为参数传递给期望该位置中的不同类型的函数(运算符在此处计算为函数)
  3. 您将变量分配给不同类型的变量
  4. 您可以使用变量copy-construct或初始化不同类型的变量

除了涉及继承的转换之外,还有三种类型的转换

  1. 内置转换(例如int-to-double)
  2. 隐式构造,其中类B定义了一个构造函数,该构造函数采用类型A的单个参数,并且不使用"explicit"关键字对其进行标记
  3. 用户定义的转换运算符,其中类A定义运算符B(如示例中所示)

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

(可能有一些我没有想到的转换情况或方法,所以如果你看到缺少的东西,请评论或编辑它们)


Ste*_*sop 6

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

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