转换构造函数和运算符都存在且涉及显式性时的行为

Behavior when both conversion constructor and operator are present and explicitness is involved

本文关键字:构造函数 运算符 存在 转换      更新时间:2023-10-16

我有一段代码,其中我有转换构造函数和转换运算符。

#include <iostream>
struct ClassFloat;
struct ClassInt{
int value;
ClassInt(int c) : value(c){std::cout << "From integern";};
ClassInt(ClassFloat x);
//explicit ClassInt(ClassFloat x);
};
struct ClassFloat{
float val;
explicit operator ClassInt() {std::cout << "Conversion operator calledn"; return ClassInt{999};}
//operator ClassInt() { std::cout << "Conversion operator calledn"; return ClassInt{999};}
};
ClassInt::ClassInt(ClassFloat x){
std::cout << "Conversion constructor called!n";
value = (int)x.val;
}
int main(){
ClassFloat floatObj{3.5f};
ClassInt instance1 = floatObj;           // (1)
ClassInt instance2 = (ClassInt)floatObj; // (2)
return 1;
}
  1. 如果两者都是非显式的。我收到一个编译器错误,说第一个表达式不明确。第二个表达式调用构造函数。
  2. 如果只有运算符是显式的,则两个转换都使用构造函数。
  3. 如果只有构造函数是显式的,则第二次转换调用构造函数,第一个转换使用运算符。
  4. 如果两者都是显式的,我只能编译第二个表达式。它使用构造函数。

我不明白为什么在第二个场景中的第二个表达式中不调用转换运算符。

我也期待第四种情况下会出现歧义错误(类似于第一个场景),但选择了构造函数。

我使用 g++ 7.4.0 编译,带有 -pedantic 和 -std=c++17 标志。

首先,c型演员表演static_cast,然后

(强调我的)

1)如果存在从expressionnew_type的隐式转换序列,或者如果直接初始化对象的重载分辨率或来自expressionnew_type类型的引用将找到至少一个可行的函数,static_cast<new_type>(expression)返回虚变量Temp初始化,就像通过new_type Temp(expression);一样, 这可能涉及隐式转换、对new_type构造函数的调用或对用户定义的转换运算符的调用。

所以给定(ClassInt)floatObj;(初始化好像ClassInt Temp(floatObj);),转换构造函数总是首选,它将直接用于构造ClassInt。应用转换运算符需要从floatObjClassInt的隐式转换(然后在概念中复制初始化临时ClassInt)。

对于第一种情况,您观察到的结果似乎与上述摘要略有不同,

  1. 如果两者都是非显式的。我收到一个编译器错误,说它不明确。

只有第一个表达式会导致歧义问题,第二个表达式将使用转换构造函数。

顺便说一句:我尝试使用 gcc 7.3.0,它给出了预期的结果。

为了回答您的问题,

我不明白为什么在第二个场景中的第二个表达式中不调用转换运算符。

因为转换构造函数是第二个表达式(c-ctyle 强制转换)的首选。

我也期待第四种情况下会出现歧义错误(类似于第一个场景),但选择了构造函数。

与上面相同,转换构造函数是第二个表达式的首选;第一个表达式会导致错误,因为转换构造函数和转换运算符都标记为explicit

另请注意,第一个表达式需要隐式转换,那么转换构造函数或转换运算符是否标记为explicit确实很重要。另一方面,第二个表达式是显式转换,它并不关心这一点。