Разрешено ли `std::function` перемещать свои аргументы?

Работая над этим вопросом, я заметил, что реализация GCC (v4.7) std::functionперемещает свои аргументы, когда они берутся по значению. Следующий код демонстрирует это поведение:

#include 
#include 

struct CopyableMovable
{
    CopyableMovable()                        { std::cout << "default" << '\n'; }
    CopyableMovable(CopyableMovable const &) { std::cout << "copy" << '\n'; }
    CopyableMovable(CopyableMovable &&)      { std::cout << "move" << '\n'; }
};

void foo(CopyableMovable cm)
{ }

int main()
{
    typedef std::function byValue;

    byValue fooByValue = foo;

    CopyableMovable cm;
    fooByValue(cm);
}
// outputs: default copy move move

Здесь мы видим, что выполняется копирование cm(что кажется разумным, поскольку по значениюпараметр берется по значению), но дальше два хода. Поскольку функцияработает с копией cm, тот факт, что она перемещает свой аргумент, можно рассматривать как незначительную деталь реализации. Однако такое поведение вызывает некоторые проблемы при использовании functionвместе с bind:

#include 
#include 

struct MoveTracker
{
    bool hasBeenMovedFrom;

    MoveTracker()
      : hasBeenMovedFrom(false)
    {}
    MoveTracker(MoveTracker const &)
      : hasBeenMovedFrom(false)
    {}
    MoveTracker(MoveTracker && other)
      : hasBeenMovedFrom(false)
    {
        if (other.hasBeenMovedFrom)
        {
            std::cout << "already moved!" << '\n';
        }
        else
        {
            other.hasBeenMovedFrom = true;
        }
    }
};

void foo(MoveTracker, MoveTracker) {}

int main()
{
    using namespace std::placeholders;
    std::function func = std::bind(foo, _1, _1);
    MoveTracker obj;
    func(obj); // prints "already moved!"
}

Разрешено ли такое поведение стандартом? Разрешено ли std::functionперемещать свои аргументы? И если да, то нормально ли, что мы можем преобразовать оболочку, возвращаемую bind, в std::functionс параметрами по значению, даже если это вызывает неожиданное поведение при работе с несколькими вхождения заполнителей?

17
задан Community 23 May 2017 в 11:52
поделиться