【发布时间】:2016-05-21 09:38:26
【问题描述】:
考虑以下类:
class foo {
int data;
public:
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(const T& i) : data{ i } { cout << "Value copy ctor" << endl; }
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(T&& i) : data{ i } { cout << "Value move ctor" << endl; }
foo(const foo& other) : data{ other.data } { cout << "Copy ctor" << endl; }
foo(foo&& other) : data{ other.data } { cout << "Move ctor" << endl; }
operator int() { cout << "Operator int()" << endl; return data; }
};
当然,以任何形式引用单个int 都没有多大意义,但这只是一个示例。 data 成员的复制成本可能非常高,因此需要所有的移动语义。
这个花哨的模板基本上支持任何可以构造data 的类型。因此,foo 对象可以通过复制或移动满足此条件的任何类型的值来构造,也可以通过复制或移动另一个foo 类型的对象来构造。到目前为止非常简单。
当您尝试执行以下操作时会出现问题:
foo obj1(42);
foo obj2(obj1);
这个应该做什么(至少在我看来)是通过将值 42 移入其中来构造第一个对象(因为它是一个右值),然后通过复制第一个对象。所以这应该打印出来的是:
Value move ctor
Copy ctor
但它实际打印出来的是:
Value move ctor
Operator int
Value move ctor
第一个对象构建得很好,没有问题。但是程序没有调用复制构造函数来构造第二个对象,而是将第一个对象转换为另一种类型(通过我们定义的转换),然后调用foo 的另一个构造函数,它可以从该类型移动(因为它是一个右值那个点)。
我觉得这很奇怪,而且这绝对不是我想要的这段代码的行为。我认为在构造第二个对象时调用复制构造函数更有意义,因为考虑到我提供的参数类型,这似乎更简单。
谁能解释这里发生了什么?当然我明白,由于有一个用户定义的到 int 的转换,这是一个完全有效的路径,但我无法理解它。为什么编译器会拒绝简单地调用与提供的值具有完全相同的参数类型的构造函数?这不是最简单的事情,因此是默认行为吗?调用转换运算符也会执行复制,所以我不认为这比简单地调用复制构造函数更快或更优化。
【问题讨论】:
-
不应该是 is_constructible
而不是你目前拥有的其他方式吗? -
@Alejandro 正确,那也是solves the issue。
-
@nwp 我很困惑。根据this,该事物返回是否可以从第二事物构造第一个事物。由于我正在从
T参数构造我的data成员(这是一个int),因此这应该是正确的顺序。我只接受可以构造int的类型。 -
你说的确实解决了问题,但我现在比以前更困惑:D
-
@Alejandro 所说的仅有效,因为 T 在示例中是 int 。如果您将数据声明为任何非原始类型(称为 bar)并且 int 不能从 bar 构造,那么这些构造函数将从 is_constructible
中排除,因为对于 T : int 然后 int 没有可见的方法来构造自身从酒吧。但是 is_constructible for T :如果 bar 声明了一个接受 int 的公共构造函数, int 将成功。如果我对用法的解释有误,请纠正我。
标签: c++ constructor type-conversion copy-constructor enable-if