2017-10-14 100 views
7

我有一个类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对它进行了测试,它显示了所描述的错误。

+0

无法重现:https://godbolt.org/g/ESR8cw – bolov

+0

@bolov很奇怪,但它并没有重现C++ 17。它在godbolt中用-std = C++ 11重现。我会将其添加到帖子中,谢谢。 –

+0

hmm ..有趣 – bolov

回答

6

之前C++ 17

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

T构造函数被检查和最佳匹配是由过载分辨率来选择。然后调用构造函数来初始化该对象。

正如你所说的,所有的std::string可能的构造进行检查和C可以转换为需要什么,然后产生歧义。

string ok = c执行copy initialization(注意这不是分配),然后

如果T是一个类类型和other类型的CV-不合格的版本不是TT衍生,或者如果T是非班级类型,但other的类型是类别类型,用户定义的转换序列可以从other类型转换为T(如果TT类型,并且转换函数是可用)进行检查a nd最好的选择是通过重载解析。

这意味着将检查从Cstd::string的转换,并将其用于初始化。

后C++ 17

由于C++ 17 direct initlizatioin

如果初始化是prvalue表达式,其CV-非限定类型是相同的类T,初始化表达式本身,而不是临时实体化,用于初始化目标对象:请参见copy elision(自C++ 17以来)

这意味着从Cstd::string的转换被优先使用并用于初始化,然后歧义消失,代码运行良好。

LIVE

+1

详细说明复制初始化([over.ics.user]/3):*如果用户定义的转换由专用的转换函数模板指定,则第二个标准转换序列应具有完全匹配的等级。我不会说这是最好的。 – chris