1

I was tinkering with a vector of std::function like this:

#include "stdafx.h"
#include <stdexcept>
#include <functional>

#include <iostream>
#include <vector>

typedef std::function<void(int)> FuncType;

std::vector<FuncType> container;

int _tmain(int argc, _TCHAR* argv[])
{

    container.push_back([](int i){std::cout << i+1 << std::endl;});
    container.push_back([](int i){std::cout << i+42 << std::endl;});

    for(auto & o : container)
    {
        o(4);
    }

    return 0;
}

which basically just returns 5 and 46 and was thinking whether I can change the declaration of the container to some sort of wrapper class but to maintain the push back of the lambdas (= not changing anything else except declaration).

Currently I tried to implement some stub wrapper doing nothing in particular which should just compile, but it seems that the conversion from lambda to Wrapper cannot be done implicitly.

#include "stdafx.h"
#include <stdexcept>
#include <functional>

#include <iostream>
#include <vector>


typedef std::function<void(int)> FuncType;
template<class T>
class Wrapper
{
public:
    Wrapper(T t)
    {
        _t = t;
    }

   void operator()(int i) const
   {
       _t(i);
   }

protected:

   T & _t;

};


std::vector<Wrapper<FuncType>> container; // Only line changed

int _tmain(int argc, _TCHAR* argv[])
{

    container.push_back([](int i){std::cout << i+1 << std::endl;});
    container.push_back([](int i){std::cout << i+42 << std::endl;});

    for(auto & o : container)
    {
        o(4);
    }

    return 0;
}

The goal here is to wrap the call to o(int)and output some diagnostics e.g. o.target_type().name() or performance values etc but without altering the push_back into the container to Wrapper (avoiding macro magic too)

Note: As I am using VS 2012 where variadic template arguments are not yet implemented, the standard MS std::function resorted to some macro magic like _VARIADIC_EXPAND_P1_1(_CLASS_FUNC_CLASS_1, , , , ) to provider operator()

Samuel
  • 6,126
  • 35
  • 70
  • Replace `push_back` by `emplace_back`? – Kerrek SB Jun 24 '14 at 08:54
  • Be aware that std::function is buggy in VS2012 ( http://stackoverflow.com/questions/22048090/difference-between-stdbind-and-boostbind-with-polymorphism ), so maybe consider boost function instead – Kiroxas Jun 24 '14 at 08:59
  • 2
    You need a templated ctor, because otherwise you need two user-defined conversions - one from lambda to `std::function`, one from `std::function` to `Wrapper`. Also, you have a dangling reference as soon as the ctor exits. – Xeo Jun 24 '14 at 08:59
  • @Xeo: templated in what terms? In terms of pushing a Wrapper instead of lambda? – Samuel Jun 24 '14 at 09:34
  • 1
    `template Wrapper(F f) : _t(f){}`, along those lines (as soon as you make `_t` a non-reference). – Xeo Jun 24 '14 at 10:00
  • @Xeo: Now I got it. Thanks. If you make it an answer I am happy to accept it :) – Samuel Jun 24 '14 at 10:18

1 Answers1

2

You're trying to go through two user-defined conversions, and that's illegal in C++. Instead, make the constructor a constrained template. See below:

#include <functional>
#include <utility>
#include <iostream>
#include <vector>
#include <type_traits>

typedef std::function<void(int)> FuncType;

template<class T>
class Wrapper
{
public:
    template<typename U,
    typename std::enable_if<
        std::is_constructible<T, U>::value,
        int
    >::type = 0>
    Wrapper(U t)
      : _t(std::move(t))
    {}

   void operator()(int i) const
   {
       _t(i);
   }

private:
   T _t;
};

std::vector<Wrapper<FuncType>> container; // Only line changed

int main(int argc, char* argv[])
{
    container.push_back([](int i){std::cout << i+1 << std::endl;});
    container.push_back([](int i){std::cout << i+42 << std::endl;});

    for(auto & o : container)
    {
    o(4);
    }
}
Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • What advantage does your code has over @Xeo simple templated ctor has? – Samuel Jun 24 '14 at 17:25
  • 1
    @Samuel: Clearer error message in case you pass something that is not storable in a `std::function`. My snippet was only meant as a hint anyways. – Xeo Jun 24 '14 at 18:10
  • 1
    @Samuel Correctness. Without the `enable_if` constraint, this would be a universal implicit converting constructor. That would be bad. `Wrapper` cannot be constructed from anything, only from those things from which we can construct a `T`. The `enable_if` captures and enforces that constraint. – Eric Niebler Jun 24 '14 at 18:18
  • This does not compile with VS2012 as default template arguments are not allowed on class template. – Samuel Jun 25 '14 at 06:34
  • Mistake in my comment above: VS2012 complains with error C4519: default template arguments are only allowed on a class template. (Note the VS2012 tag for this thread.) While VS2013 compiles fine here. So this will be my wrapper solution for MSVER >1700. The wrapper itself is not meant to be used in a broad context, so basically a loose constraint on U is tolerable IMHO. – Samuel Jun 25 '14 at 06:49
  • 1
    Try: `template Wrapper(U t, typename std::enable_if< std::is_constructible::value, int>::type = 0) : _t(std::move(t)) {}`. And switch to clang. ;-) – Eric Niebler Jun 25 '14 at 17:45