3

我有一个模板类,我想避免复制(因为这样做的潜在成本)。我可以实现一个移动构造函数,但我也想允许移动“跨模板参数”。这是我要编译的内容:

template <class T>
class Foo
{
public:
    Foo() {}
    template <class U> Foo(Foo<U>&&) {}

private:
    Foo(const Foo&);
};

Foo<int> f() { Foo<float> y; return move(y); }
Foo<int> g() { Foo<int> x; return x; }
Foo<int> h() { Foo<float> z; return z; }

我理解为什么从技术上讲 f 编译:move(y) 的类型是 Foo(float)&& 并且恰好有一个方便的构造函数接受 Foo(U)&&,因此编译器设法找到 U=float 有效。

h 不编译。z 是 Foo(float) 类型,我想这离 Foo(U)&& 太远了,无法确定如果选择了 U=float 则可以调用移动构造函数......

我不知道为什么 g 编译,但确实如此。x 的类型是 Foo(int)。编译器如何设法使用移动运算符(它不能只是从 Foo(int) 隐式转换为 Foo(int)&&,可以吗?)

所以我的问题是:规则是什么?为什么 h 编译但 g 不编译?有什么我可以在 Foo 中更改以使 h 编译的吗?

谢谢

4

1 回答 1

7

复制或移动构造函数不能是模板。从 12.8(2, 3) 开始:

如果类的第一个参数是、或类型,并且没有其他参数或者所有其他参数都具有默认参数(8.3.6),则类的非模板构造函数是一个复制构造函数。[示例:and是复制构造函数。]XX&const X&volatile X&const volatile X&X::X(const X&)X::X(X&,int=1)

类的非模板构造函数X是移动构造函数,如果其第一个参数的类型为X&&const X&&volatile X&&const volatile X&&,并且要么没有其他参数,要么所有其他参数都有默认参数(8.3.6)。[示例:Y::Y(Y&&)是一个移动构造函数。]

因此,您的示例fg工作是因为您正在调用一个普通的构造函数(而不是move构造函数)。

f工作的原因很明显,因为move(y)can bind to的结果Foo<float>&&g出于不同的原因:由于类型x与函数的返回类型相同,x因此返回语句中的表达式的值匹配Foo<int>&&。这是因为 12.8(31, 32):

在具有类返回类型的函数的语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型return的非易失性自动对象(函数或 catch 子句参数除外)的名称时,[ ...]

当满足或将满足复制操作的省略标准时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。

最后我们看到为什么h不起作用:语句中的表达式的值z不能return绑定到Foo<float>&&,因为它没有显式强制转换(通过std::move),也没有给出第 12.8(32) 条的特殊规定,因为它的类型不同作为函数的返回类型。(它只能绑定到Foo<float>&(这几乎肯定是错误的)或绑定到Foo<float> const &。)


顺便说一句,移动不管理外部资源(例如原语)的对象是没有意义的。无论如何都必须复制实际的对象数据。

于 2012-06-14T08:04:28.593 回答