4

Using Visual Studio 2013 RC and C++, I'm trying to pass an std::unique_ptr to a function that has been bound using std::bind. However, I'm having trouble because VS doesn't seem to like it when I try this. Here's what I'm trying to compile:

#include <memory>
#include <iostream>
#include <functional>

void func(std::unique_ptr<int> arg)
{
    std::cout << *arg << std::endl;
}

int main()
{
    std::function<void (std::unique_ptr<int>)> bound =
        std::bind(&func, std::placeholders::_1);

    std::unique_ptr<int> ptr(new int(42));
    bound(std::move(ptr));

    return 0;
}

This compiles in GCC 4.8.1, but not in VS2013 RC. I've always had problems with move semantics in VS, but I'd really like to use std::unique_ptr instead of std::shared_ptr or raw pointers.

One workaround I've found is to change the function signature to accept an std::unique_ptr&, which does compile in VS and GCC, but doesn't make the intent of func taking ownership of the std::unique_ptr particularly clear, and also prevents me from safely asynchronously calling the function unless I do something particularly ugly:

#include <memory>
#include <iostream>
#include <functional>
#include <future>
#include <string>

void func(std::unique_ptr<int>& arg)
{
    std::cout << *arg << std::endl;
}

int main()
{
    std::function<void (std::unique_ptr<int>&)> bound =
        std::bind(&func, std::placeholders::_1);

    std::unique_ptr<int> ptr(new int(42));
    std::promise<void> prom;
    std::async(
        [&bound, &ptr, &prom]
        {
            std::unique_ptr<int> movedPtr = std::move(ptr);
            prom.set_value();

            bound(std::move(movedPtr));
        });

    prom.get_future().wait();

    // Wait here
    std::string dummy;
    std::cin >> dummy;
}

Is there a way to get around this without changing func's signature?

Thanks!

  • you cannot pass unique_ptr by value, it doesn't have copy constructor. – yngccc Oct 11 '13 at 01:19
  • 1
    Hence the `std::move()`. Calling `func(std::move(ptr))` works perfectly fine if I call it directly, but not when it's bound. –  Oct 11 '13 at 01:20
  • then why not change `func` to accept `unique_ptr&&` instead – yngccc Oct 11 '13 at 01:23
  • 1
    Because it doesn't compile in VS2013... Having the argument be `std::unique_ptr` vs. `std::unique_ptr&&` makes no practical difference whatsoever in this case. –  Oct 11 '13 at 01:25

3 Answers3

2

I had the same problem with VS 2012 recently. I believe this is a bug in MSVC; at least in MSVC++11 the pseudo-variadic expansion seems to forward the parameters by value to some internal function. Seems this hasn't been improved.
As a workaround, I'm using lambdas instead, but another hack is required to make it work:

std::function<void (std::unique_ptr<int>)> bound =
    [] (std::unique_ptr<int> arg) { func(std::move(arg)); };

still doesn't compile. But if you add any captured value (even one that isn't used), it compiles:

int x;
std::function<void (std::unique_ptr<int>)> bound =
    [x] (std::unique_ptr<int> arg) { func(std::move(arg)); };
cdoubleplusgood
  • 1,309
  • 1
  • 11
  • 12
  • Ah, that's exactly what I need! It should be easy to ifdef out for Linux targets as well. Thank you so much! –  Oct 11 '13 at 12:33
  • You just saved me a redesign effort. Thanks. I know that GCC 4.7 handles placeholders properly as well. – Mranz Feb 20 '14 at 06:54
  • The hack with the fake captured value is no longer required in VS 2013 (VC12). – cdoubleplusgood Feb 21 '14 at 19:59
1

You have to move the parameter into the bound call to func also. Not only in the invocation of bound

bound(std::move(ptr));

but also in the binding:

std::function<void(std::unique_ptr<int>)> bound =
    std::bind(func,
              std::bind(std::move<std::unique_ptr<int>&>,
                        std::placeholders::_1));

This is compiling in VS2013 (update 4) for me.

John
  • 7,301
  • 2
  • 16
  • 23
0

Functions bound with std::bind do not forward arguments, it copies them to the function. As a result, std::bind doesn't work with move-only types as of c++11. This problem is the idea behind proposals for "more perfect forwarding" (like this one). There's a newer one, but I can't seem to find it right now.

JKor
  • 3,822
  • 3
  • 28
  • 36
  • That may be true for functions which have arguments bound into the `std::function`, but the `std::function` I'm trying to use has only placeholders. My first code snippet compiles in GCC, but not VS. I'm trying to find a workaround so I can keep `func`'s signature and still compile with VS. –  Oct 11 '13 at 01:54