移动成员函数生成

时间:2013-06-03 13:25:32

标签: c++ c++11 language-lawyer

代码:

#include <iostream>
#include <ios>
#include <string>
#include <type_traits>
#include <memory>

struct value
{
    ~value() = default;
    std::unique_ptr<std::string> s;
};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_move_constructible<value>::value << '\n';
    std::cout << std::is_move_assignable<value>::value    << '\n';

    using str_ptr = std::unique_ptr<std::string>;
    std::cout << std::is_move_constructible<str_ptr>::value << '\n';
    std::cout << std::is_move_assignable<str_ptr>::value    << '\n';

    return 0;
}

输出(使用g ++ v4.7.2编译,http://ideone.com/CkW1tG):

false
false
true
true

正如我所料,value不是可移动的,也不是可移动的,因为:

~value() = default;

用户声明的析构函数,它根据 12.8 部分(见下文)阻止隐式生成移动成员。 如果析构函数被删除,则value可移动可构造并移动可分配,如我所料(http://ideone.com/VcR2eq)。

但是,当value的定义更改为(http://ideone.com/M8LHEA)时:

struct value
{
    ~value() = default;
    std::string s;      // std::unique_ptr<> removed
};

输出是:

true
true
true
true

value意外地移动可构造并移动可分配。 我误解了还是编译器错误?


背景:我提供了对this问题的回答,并被告知Tree<>是可移动的,但我不确定并且正在尝试确定它是否是或不


部分 8.4.2 c ++ 11标准的显式默认函数(草案n3337):

  

明确默认的函数和隐式声明的函数   统称为违约函数,并实施   为它们提供隐含定义(12.1 12.4,12.8),这可能意味着   将它们定义为已删除。   如果是用户声明的特殊成员函数,则由用户提供   未在第一次声明中明确默认或删除。   用户提供的明确默认功能(即明确默认)   在第一次声明之后)定义在它的位置   明确违约;如果将这样的函数隐式定义为已删除,   该计划格式不正确。 [注意:声明函数默认后的默认值   第一个声明可以提供有效的执行和简洁的定义   为稳定的代码库提供稳定的二进制接口。-end note]

Section 12.8复制和移动类对象(第9点):

If the definition of a class X does not explicitly declare a move constructor,
one will be implicitly declared as defaulted if and only if
- X does not have a user-declared copy constructor,
- X does not have a user-declared copy assignment operator,
- X does not have a user-declared move assignment operator,
- X does not have a user-declared destructor, and
- the move constructor would not be implicitly defined as deleted.

1 个答案:

答案 0 :(得分:9)

如果std::is_move_constructible<T>为真,则

std::is_constructible<T, T&&>为真,但这并不意味着这样的构造将调用移动构造函数,只是可以从相同的rvalue构造类型类型。这样的构造可能使用复制构造函数。

value::sunique_ptr时,类型的复制构造函数和复制赋值运算符被定义为已删除,因为s成员不可复制。它没有移动构造函数和移动赋值运算符,因为正如您所指出的,它具有用户声明的析构函数。这意味着它没有复制构造函数,也没有移动构造函数(也没有其他用户定义的构造函数可以接受value&&类型的参数),因此std::is_constructible<value, value&&>为假。

value::sstring时,类型的复制构造函数和复制赋值运算符定义为已删除,因为s成员为< / strong>可复制,因此value也是可复制的,而CopyConstructible类型也是MoveConstructible,因为它在此上下文中有效:

value v1;
value v2 = std::move(v1);  // calls copy constructor

这意味着std::is_constructible<value, value&&>为真,即使它调用复制构造函数而不是移动构造函数。