Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::function for not MoveConstructible function object in gcc

Tags:

c++

gcc

c++11

I'm trying to get along with std::function. From reference here one can see that argument to std::function's ctor should be callable and copy constructible. So here is small example:

#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;
}

We have callable, copy constructible but not move constructible class. As I believe this should be sufficient to wrap it in Function. But if you uncomment commented line compiler becomes very upset about deleted move constructor. Here is ideone link. GCC 4.8.0 doesn't compile this too.

So, is it something I misunderstand about std::function or is it GCC's incorrect behaviour?

like image 582
iw.kuchin Avatar asked Jul 25 '13 07:07

iw.kuchin


2 Answers

GCC and Clang are correct.

§17.6.3.1.1 Template argument requirements [utility.arg.requirements]

Table 20MoveConstructible requirements [moveconstructible].

  • T u = rv; u is equivalent to the value of rv before the construction.
  • T(rv); T(rv) is equivalent to the value of rv before the construction.

Table 21CopyConstructible requirements (in addition to MoveConstructible) [copyconstructible].

  • T u = v; the value of v is unchanged and is equivalent to u.
  • T(v); the value of v is unchanged and is equivalent to T(v).

Note the:

CopyConstructible requirements (in addition to MoveConstructible)

I.e. if something is CopyConstructible it must also be MoveConstructible. Though it is fine to implement the move as a copy.

Update:

Though I find it interesting that the C++11 standard doesn't seem to define is_copy_constructible in terms of CopyConstructible, i.e. they are not quite the same, is_copy_constructible is more relaxed as it only requires:

§20.9.4.3 Type properties [meta.unary.prop]

Table 49 — Type property predicates

  • is_copy_constructible<T>; is_constructible<T,const T&>::value is true.
like image 128
ronag Avatar answered Sep 28 '22 08:09

ronag


You misunderstood the purpose of the delete specification. A move-constructor is not default-implemented. If you try to move an object without a move-ctor it will just be copied. If you specify the move-constructor as deleted it will try to call it and then see it's deleted. That means: You cannot copy a temporary object. Remove the move-constructor statement and it will work.

Edit - Clarification:

If no move-constructor is declared(where =delete is a declaration), then a construction from a temporary object will call the copy-constructor (from const reference). If you declare a move constructor a construction from a temporary object will try to call this. So if you declare a move-ctor deleted, it will try to call a delete function, which will result in an error. If you do not declare it, it will result in a call of the copy ctor.

That's why your example doesn't work, if you wouldn't have declared it std::function would just use the copy-ctor.

By default I meant: implemented if not declared, as it happens with the copy- or default-ctor.

like image 37
Klemens Morgenstern Avatar answered Sep 28 '22 08:09

Klemens Morgenstern