gcc中不可移动构造函数对象的std::函数

std::function for not MoveConstructible function object in gcc

本文关键字:std 对象 函数 构造函数 可移动 gcc      更新时间:2023-10-16

我正在努力与std::function相处。从这里的引用可以看出,std::function的ctor的参数应该是可调用的,并且是可复制构造的。这里有一个小例子:

#include <iostream>
#include <type_traits>
#include <functional>
class A {
public:
    A(int a = 0): a_(a) {}
    A(const A& rhs): a_(rhs.a_) {}
    A(A&& rhs) = delete;
    void operator() ()
    {
        std::cout << a_ << std::endl;
    }
private:
    int a_;
};
typedef std::function<void()> Function;
int main(int argc, char *argv[])
{
    std::cout << std::boolalpha;
    std::cout << "Copy constructible: "
              << std::is_copy_constructible<A>::value << std::endl;
    std::cout << "Move constructible: "
              << std::is_move_constructible<A>::value << std::endl;
    //Function f = A();
    return 0;
}

我们有可调用的、可复制的、但不能移动的可构造类。因为我认为这应该足以将其封装在Function中。但若取消注释注释行,编译器会对删除的move构造函数感到非常不安。这是视频链接。GCC 4.8.0也没有对此进行编译。

那么,这是我对std::function的误解,还是GCC的错误行为?

GCC和Clang是正确的。

§17.6.3.1.1模板参数要求[实用程序参数要求]

表20--MoveConstructable需求[moveConstructable]

  • T u=rv;u等于rv的值施工前
  • T(rv);T(rv)等于rv的值施工前

表21--除了MoveConstructible)[可复制构造]

  • T u=v;v的值不变,等于u
  • T(v);价值v不变并且等价于T(v)

注意:

CopyConstructable要求(除MoveConstructable外)

也就是说,如果某个东西是CopyConstructible,它也必须是MoveConstructible。尽管将移动作为副本来实现是可以的。

更新:

虽然我觉得有趣的是,C++11标准似乎没有根据CopyConstructable来定义is_copy_constructible,即它们不完全相同,但is_copy_constructible更宽松,因为它只需要:

§20.9.4.3类型属性[meta.unary.prop]

表49——类型属性谓词

  • is_copy_constructible<T>;is_constructible<T、 常量T&gt;:值为true

您误解了删除规范的目的。移动构造函数不是默认实现的。如果你试图在没有移动ctor的情况下移动一个对象,它只会被复制。如果您将move构造函数指定为已删除,它将尝试调用它,然后查看它是否已删除。这意味着:不能复制临时对象。删除move构造函数语句,它就会工作。

编辑-澄清:

如果没有声明移动构造函数(其中=delete是一个声明),则来自临时对象的构造将调用复制构造函数(来自const引用)。如果您声明一个move构造函数,则来自临时对象的构造将尝试调用此构造函数。因此,如果您声明move-ctor已删除,它将尝试调用delete函数,这将导致错误。如果您不声明它,它将导致对复制ctor的调用。

这就是为什么您的示例不起作用的原因,如果您不声明它,std::函数将只使用copy ctor。

默认情况下,我的意思是:如果未声明,则实现,就像使用复制或默认ctor一样。