【问题标题】:Different behavior among explicit cast, direct initialization and copy initialization显式强制转换、直接初始化和复制初始化之间的不同行为
【发布时间】:2017-10-14 02:47:14
【问题描述】:

我有一个类C,它有一个对任何东西的强制转换运算符。在示例中,我尝试以三种不同的方式将其实例转换为std::stringstatic_caststd::string 的构造函数和分配给std::string。但是,只有最后一个编译,而其他的则引发了构造函数不明确的错误。

错误的原因很清楚:有很多方法可以将C 转换为std::string 的构造函数可以接受的东西。但是这些案例之间有什么区别呢?为什么 cast 运算符在这里按预期工作,但在那儿不按预期工作?

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 在 cmets 中提到的,这个问题不会在 C++17 中重现。我用 g++-5 和 clang-3.8 用 -std=c++11 和 -std=c++14 对其进行了测试,它显示了所描述的错误。

【问题讨论】:

  • 无法复制:godbolt.org/g/ESR8cw
  • @bolov 很奇怪,但它不会在 C++17 中重现。它使用 -std=c++11 在 Godbolt 上重现。我会把它添加到帖子中,谢谢。
  • 嗯..有趣
  • @bolov 在我这边不能在 gcc/clang 上编译,参见例如here。是的,确实 g++ -std=c++17 接受了代码。
  • @vsoftco 不。如果你也添加operator const char*,歧义会返回。现在没有模板。

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


【解决方案1】:

C++17 之前

static_cast&lt;string&gt;(c)string bad(c) 执行 direct initialization,然后

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

正如你所说,std::string 的所有可能构造函数都经过检查,C 可以转换为所需的任何内容,然后导致歧义。

string ok = c 执行copy initialization(注意不是赋值),然后

如果T 是类类型,并且other 类型的 cv 非限定版本不是T 或派生自T,或者如果T 是非类类型,但是other 的类型是类类型,用户定义的转换序列,可以从other 的类型转换为T(或者如果T 是类类型并且转换为从T 派生的类型功能可用)进行检查,并通过重载决议选择最佳的。

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

C++17 之后

因为direct initlizatioin,C++17,

如果初始化器是一个纯右值表达式,其 cv-unqualified 类型与T 相同,则初始化器表达式本身,而不是从它的临时物化,用于初始化目标对象:参见copy elision (从 C++17 开始)

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

LIVE

【讨论】:

  • 详述复制初始化([over.ics.user]/3): 如果自定义转换是通过一个转换函数模板的特化指定的,则第二个标准转换序列应该有完全匹配的排名。我不会说它是首选的。
猜你喜欢
  • 2018-10-29
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多