3

I'm having a little trouble using a function object create an instance of std::function. (My environment is C++ 11 using Visual Studio 2015.)

First, I have a class that is used in the argument list of my function object. Here is a simplified version that illustrates the essential point.

class X
{
public:
    void zz(int a) const
    {
        std::cout << "X:zz, a = " << a << "." << std::endl;
    }
};

Then here is the function object, which uses X in its operator() method.

class Y
{
public:
    void operator()(const X& x)
    {
        x.zz(17);
    }
};

Now, I'd like to wrap an instance of Y in a std::function. The type of the function is void(const X&), a void function taking a const reference to X as an argument. The method Y:operator() has this signature.

void test_it
{
    X my_x;

    Y my_y;
    std::function<void(const X&)> f1(my_y);
    f1(my_x); // OK

    std::function<void(const X&)> f2(Y());
    f2(my_x); // cannot convert argument 1 from X to Y

    std::function<void(const X&)> f3(Y);
    f3(my_x); // cannot convert argument 1 from X to Y

    std::function<void(const X&)> f4(Y{});
    f4(my_x); // OK
}

The first example, f1, creates an instance of Y, my_y, and creates f1 using this instance. This works fine. (The contents of Y get moved into the std::function in the std::function constructor.)

The second try, f2, constructs an instance of Y in the constructor for f2. When I try to invoke it with f2(my_x) the compiler complains that it cannot convert argument 1 (my_x) from an X to a Y.

The third try, f3, where I just name the function object class in the std::function construction of f3 fails in the same way as f2.

Finally, f4 works. In this one, I construct the function object Y using an empty initializer list.

Perhaps there is a simple explanation, but I cannot see why two of these use-cases don't compile. In particular f2, using Y(), and f4 using Y{}, seem virtually identical to me.

cha
  • 207
  • 1
  • 2
  • 9

1 Answers1

5

For the second case, the expression Y() is trying to define a function that returns a Y and that takes no arguments, and passing that function as the argument. This is known as the most vexing parse.

For the third case, you cannot give a type instead of a function argument.

The correct way to pass a default constructed instance of Y to std::function is the 4th case.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • Thanks for the fast answer. What an appropriate name, "vexing parse". – cha Jul 21 '17 at 15:58
  • BTW, I found while playing around after I posted, if I give the Y class a dummy int argument for its constructor, so all the Y's have to be initialized as Y(0), or Y{0}, then case 2 works using "f2(Y(0))" to construct f2 with an instance of Y as its argument. This disambiguates the situation, also. In any event, the 4th case is the easiest to use. Thanks!! – cha Jul 21 '17 at 16:02
  • @cha If you want to use the 2nd case, you can simply wrap expression in a set of parenthesis. `f2((Y()));` – François Andrieux Jul 21 '17 at 16:05
  • Thanks. The 4th form should be just as convenient, but it's good to know that workaround. What's even better is understanding why the original code for the 2nd example was ambiguous, and why either the extra parentheses or the initializer braces removed the ambiguity. – cha Jul 21 '17 at 18:26