polymorphic unique_ptr copy elision

时间:2018-01-04 15:21:14

标签: c++ c++14

我有以下代码适用于Clang 5.0但不适用于Clang 3.8,启用了C ++ 14:

class Base {};
class Derived : public Base {};

std::unique_ptr<Base> MakeDerived()
{
    auto derived = std::make_unique<Derived>();
    return derived;
}

int main()
{
    auto base = MakeDerived();
    std::cout << "Type: " << typeid(base).name() << '\n';
}

Live Sample Here

在这种情况下,由于复制省略,返回值是否在技术上是移动构造的?如果是这样,这是否意味着unique_ptr的移动构造函数旨在支持用户类类型的隐式向上转换?从cppreference documentation(#6)很难说,除非我在该文档页面上假设模板类型U明显不同于类本身的类型T

显而易见的一点是,unique_ptr不是可复制构造的,因为我没有通过std::move()将其转换为右值,所以没有其他解释为什么代码将编译除复制elision之外的其他代码。但是因为它不适用于接受-std=c++14标志的clang 3.8,我想确保标准本身保证某个地方(如果是这样,那里),这只是一个编译器错误或缺乏支持在Clang的第3.8节中的问题。

2 个答案:

答案 0 :(得分:4)

复制elision(在C ++ 17之前)总是要求elided构造函数实际可访问,因此它不能成为代码工作的原因。

但请注意,C ++(自C ++ 11以来)有一条规则(12.8 / 32),归结为以下内容:“返回对象时,然后在某些情况下,首先尝试将对象视为右值,只有当它失败时,将其视为左值。“

在C ++ 11中,这些“特定情况”是“复制省略是可能的”,这要求返回的对象和返回类型是相同的类型(模数cv限定)。

在C ++ 14中,这些“特定情况”被放宽为“可以复制省略,或者返回的对象是函数中的局部变量。”

所以在C ++ 11中,代码失败了,因为std::unique_ptr<Derived>derived的类型)与std::unique_ptr<Base>(返回类型)不同,因此{{1}必须使用。

在C ++ 14中,代码成功,因为std::move是函数中的局部,因此首先将其视为右值。这使得您引用的构造函数模板适用:

如果derived可以转换为std::unique_ptr<T>,则可以使用std::unique_ptr<U>类型的右值构建

U*

你的Clang 3.8似乎表现出C ++ 11的行为;也许它(尚未完全)在该版本中实现了C ++ 14的宽松方面。

(是的,确实你应该假设“模板类型”T*与类的模板参数U不同。这就是它首先被引入的原因:构造函数由#6描述的是构造函数模板)。

答案 1 :(得分:2)

return调用unique_ptr转换移动构造函数模板template<class U, class E> unique_ptr(unique_ptr<U, E>&&)。没有任何缺点;它取决于derived的解决方案启用{{1}}的隐式移动。