【问题标题】:C++11 implicitly convertC++11 隐式转换
【发布时间】:2014-06-16 10:52:52
【问题描述】:
#include <string>

struct String
{
    template<typename T> operator T*() { return 0; }
    operator std::string() { return ""; }
};

int main()
{
    String myStr;

    std::string str1(myStr); // ambiguous, error C2668

    std::string str2 = myStr; // error C2440:
    // 'initializing' : cannot convert from 'String' to
    // `std::basic_string<char,std::char_traits<char>,std::allocator<char>>',
    // No constructor could take the source type,
    // or constructor overload resolution was ambiguous

    const std::string& rStr = myStr; // Ok, but why?
}

我正在使用 VS 2013。

问题:

  1. 为什么str1str2的定义会导致不同的编译错误?

  2. 据我所知,在创建rStr 时,首先会创建一个临时字符串对象,然后rStr 将引用该临时对象。但是,为什么临时对象的创建不会导致编译错误? tmpstrN有什么不同吗?

【问题讨论】:

  • 据我所知,第二个错误是一个错误。 g++和clang++接受str2的定义+初始化。

标签: c++ c++11 implicit-conversion


【解决方案1】:

第一个定义std::string str1(myStr);确实是模棱两可的:

std::string str1(myStr.operator char*());
// or
std::string str1(myStr.operator std::string());

所以这个初始化由于歧义而失败。

这与

的场景基本相同
void foo(char const*);
void foo(std::string);

foo(myStr); // ambiguous

只需要一个用户定义的转换,然后一个函数将被调用(对于第一个定义,该函数是一个构造函数)。两种转换都是可行的,而且两者都不是另一个的子集,因此它们的排名相同。


第二个定义,std::string str2 = myStr; 实际上很好。只允许一个用户定义的 std::string 的转换,可以通过构造函数或通过转换函数,不能同时通过两者。所以只有std::string str2 = myStr.operator std::string(); 是可行的。

注意string str2 = expr;expr 不是string 类型时,需要将expr转换为std::string。然后使用生成的临时值通过复制/移动初始化str2

string str2 = string(expr);
//            ~~~~~~ implicit

因此,右侧的转换必须直接转换std::string,否则您需要一连串两个用户定义的转换来初始化临时:(UDC = User-Defined转换)

string str2 = string(expr);
// resolved as:
string str2 = expr.operator string();        // fine: one implicit UDC
string str2 = string(expr.operator char*()); // error: two UDCs

例如,exprchar const* 通过 operator char* 然后通过转换构造函数到 std::string 需要两个用户定义的转换链 => ​​不可行。如果我们尝试使用operator char*() 转换,我们需要一个额外的构造函数隐式 构造函数调用来使RHS 成为string

这与string str1( expr ) 不同,其中expr 不需要隐式 转换为string可能必须转换为初始化字符串构造函数的参数。从可能转换expr 直接初始化str1 本身并不是一个(n 隐式)转换,而只是一个函数调用。没有额外的临时创建:

string str1( expr );
// resolved as:
string str1( expr.operator string() ); // fine
string str1( expr.operator char* () ); // fine

在编译 启用语言扩展时,第二个定义被拒绝。没有语言扩展,这个初始化在 VS2013 Update 2 中很好。


第三个遵循不同的初始化方案。据我所知,在这种情况下,它应该表现得像第二个。语言扩展似乎只适用于第二个而不适用于第三个。

【讨论】:

  • 可能扩展的行为与to this behaviour when assigning to a std::string有关。
  • 为什么在第一种情况下的歧义,非模板函数不是首选,从而避免了模板化运算符的实例化?
  • @icepack 非模板与模板消歧仅适用于选择目标函数,不适用于转换 AFAICT。两个(所有三个,复制+移动)可行的构造函数都是非模板。
  • 第二个定义,有没有这样的,std::string str2(myStr->operator char*) or std::string str2(myStr->operator std::string);跨度>
  • @LeonhartSquall 抱歉,我不太明白你的意思。我替换了用于显示哪些转换是通过可能的不那么模棱两可的函数调用执行的宽松语法。第二个定义在功能上等同于std::string str2( myStr.operator std::string() );。即构造一个临时的std::string作为转换函数的返回值;然后将此临时移动到str2
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多