为什么转换构造函数比转换运算符更受青睐?

3

我有一个名为SmallInt的类,它应该表示范围在0-255之间(包括0和255)的正整数值:

struct SmallInt{
    explicit SmallInt(int x = 0) : iVal_( !(x < 0 || x > 255) ? x :
    throw std::runtime_error(std::to_string(x) + ": value outbounds!")){}
    operator int&() { return iVal_; }
    int iVal_;
};

int main(){

    try{
        SmallInt smi(7);
        cout << smi << '\n';
        cout << smi + 5 << '\n'; // 7 + 5 = 12
        cout << smi + 5.88 << '\n'; // 7.0 + 5.88 = 12.88
        smi = 33; // error: constructor is explicit
        smi.operator int&() = 33;
        cout << smi << '\n';
    }
    catch(std::runtime_error const& re){
        std::cout << re.what() << '\n';
    }
}
  • 让我关心的是:为什么我可以显式调用operator int&将值赋给smismi.operator int&() = 33,但不能隐式赋值:smi = 33;

  • 第一个表达式(smi = 33;)会报错,提示构造函数SmallInt(int)使用了explicit;我知道这一点,但我有一个返回可修改普通左值引用的转换运算符。因此换句话说,在这样的隐式赋值中为什么构造函数优先于转换运算符?


1
这个问题可能应该加上 language-lawyer 标签。 - Eljay
猜测:33int&& 类型,而不是 int& - Raildex
2
@Raildex 这是一个类型为 int 的 prvalue。表达式不能具有引用类型。 - HolyBlackCat
5
我认为 smi = 33; 被解释为 smi.operator=(33);,这需要将 33 转换为 SmallInt 以便传递参数,并因此寻找合适的构造函数。 - Ruslan
1
如果你添加一个显式类型转换,它就可以工作:(int&) smi = 33;,所以我猜它只尝试转换操作符=的右侧,这是有道理的,因为你不想转换要分配的值。 - laenNoCode
显示剩余8条评论
1个回答

6

[over.match.oper]/4 对于内置赋值运算符,左操作数的转换受到以下限制:
...
(4.2) — 不会对左操作数应用用户定义的转换以使其与内置函数候选的最左侧参数类型匹配。

因此,(int &)smi = 33 的解释被标准明确禁止考虑。


所以,尽管这个 static_cast<int&>(smi) = 57; 能够工作,但这是否意味着它是非标准的?我不应该这样做吗? - Itachi Uchiwa
2
这里讨论了重载决议和隐式转换序列被视为同一部分。当然,您也可以进行显式转换。 - Igor Tandetnik
好的,谢谢! - Itachi Uchiwa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接