Search code examples
c++boostfunctional-programmingc++17function-object

Does Boost (or another library) offer a way to lift the name of a "constructor-less" class into a function object that uses aggregate initialization?


This is kind of a follow up to this question, where I asked how I could tersely turn a template and/or overloaded function into a function object.

The accepted answer was you can't do without macro, which is correct. Then I found that such a macro is offered by Boost, in the form of the BOOST_HOF_LIFT and BOOST_HOF_LIFT_CLASS macros.

It turns out, however, that there are other "named things" you can't pass around. I don't know all of them, but one of them is constructors. And Boost.Hof offers a way to lift them to, via boost::hof::construct.

The point is that not even boost::hof::construct can deal with a class without a user-declared constructor. For intance, given

struct Foo {
    int foo;
};

the call boost::hof::construct<Foo>()(3) simply doesn't work. (Adding the constructor Foo(int) {} in Foo makes it work; that's what boost::hof::construct is for, after all.)

Surely, in simple cases like the one above I could just write

auto makeFoo = [](int x){ return Foo{x}; };

but if I want to support any type, I have to take care of perfect forwarding and variadic arguments.

Is there a library offering this feature already? It doesn't look like Boost.Hof does...


Solution

  • If you want a function object that constructs an object of some type T given some parameters, even if T is an aggregate, that's not difficult to write in C++17:

    template<typename T>
    struct lifted_construct
    {
      template<typename ...Args>
      T operator() (Args&& ...args)
      {
        if constexpr(std::is_aggregate_v<T>)
        {
          return T{std::forward<Args>(args)...};
        }
        else
        {
          return T(std::forward<Args>(args)...);
        }
      }
    };
    

    Of course, in C++20, you can use () syntax even for aggregates.