显式转换,直接初始化和复制初始化之间的不同行为

时间:2017-10-14 02:47:14

标签: c++ templates casting initialization language-lawyer

我有一个类C,它有一个任何东西的转换操作符。在示例中,我尝试以三种不同的方式将其实例强制转换为std::stringstatic_caststd::string的构造函数并分配给std::string。但是,只有最后一个编译,而其他人会引起模糊构造函数的错误。

错误的原因很明显:有很多方法可以将C转换为std::string的构造函数可以接受的内容。但这些案件有什么区别?为什么演员在这里按预期工作但不在那里工作?

struct C {
    template<typename T>
    operator T() const {
        return T{};
    }
};

int main() {
    C c;

    cout << static_cast<string>(c) << endl; // compile error
    string bad(c); // compile error
    string ok = c; // compiles successfully
}

UPD :正如bolov在评论中提到的,这个问题并没有用C ++ 17重现。我用g ++ - 5和clang-3.8用-std = c ++ 11和-std = c ++ 14测试它,它显示了所描述的错误。

1 个答案:

答案 0 :(得分:6)

在C ++ 17之前

static_cast<string>(c)string bad(c)执行direct initialization,然后

  

检查T的构造函数,并通过重载决策选择最佳匹配。然后调用构造函数来初始化对象。

正如你所说,std::string的所有可能的构造函数都会被检查,C可以转换为任何需要的构造函数,然后引起歧义。

string ok = c执行copy initialization(请注意,这不是作业),然后

  

如果T是类类型,并且other类型的cv-nonqualified版本不是T或来自T,或{{1} }}是非类型,但T的类型是类类型,用户定义的转换序列,可以从other的类型转换为other(或类型)如果T是类类型并且转换函数可用,则从T派生,并且通过重载决策选择最佳的一个。

这意味着会检查从TC的转换,并在此处用于初始化。

在C ++ 17之后

direct initlizatioin的C ++ 17以来,

  

如果初始化程序是一个prvalue表达式,其cv-unqualified类型与std::string是同一个类,则初始化程序表达式本身,而不是从中实现的临时表达式,用于初始化目标对象:请参阅{{ 3}}(自C ++ 17起)

这意味着从T转换为C并将其用于初始化,然后模糊性消失,代码运行良好。

copy elision