0

Is there any way to assign a constructor to a function like 'f':

struct C
{
    C(int){};
};

auto f = C::C(int); // not work
auto g = [](int){ return C(int()); }; // works

Neither C, C::C, &C, &C::C, C::&C, C::C(int), &C::C(int) ... works

Solution proposed by Friday Pie:

template <class T>
struct ConstructHelper
{
    template <class... Args>
    static T&& construct(Args&&... args)
    {
        return std::move(T(std::forward<Args>(args)...));
    }
};

auto f = ConstructHelper<C>::construct<int>; // works
auto c = apply(f, 0); // works
Midori Yakumo
  • 184
  • 12
  • 2
    Possible duplicate of [Construct std::function with a constructor based on template](https://stackoverflow.com/questions/12386725/construct-stdfunction-with-a-constructor-based-on-template) and/or [Assign a constructor to an std::function variable](https://stackoverflow.com/q/49661673/364696). – ShadowRanger Sep 25 '19 at 04:53
  • 1
    What you are trying to do does not make sense. – Eugene Sep 25 '19 at 04:57
  • @Eugene similar to `apply(func, args...)`, we should have `apply(cls, args...)` in c++ – Midori Yakumo Sep 25 '19 at 05:09
  • Are you asking for `allocator_traits::construct`? But then: who provides the memory location where `C` is constructed in? That's the reason it makes no sense. – j6t Sep 25 '19 at 05:38
  • A constructor does not have a type nor an address, and cannot be explicitly called in a function-call-expression. It does not return anything, not even `void`. – n. m. could be an AI Sep 25 '19 at 10:00

2 Answers2

1

The code you tried to write follows other rules than than the lambda that you wrote.

In the first one, you are trying to get a pointer to a member function: &C::C This would be similar to &C::print (assuming argument int) The first argument of this function would be a pointer to C, the second would the the int.

However, we are trying to do this for the constructor. So we don't have a valid C to work on. The result is that this simply doesn't compile.

Note that if you do want to execute a constructor on existing memory, you need placement new.

Your second code is a lambda. In short: a class with operator() implemented. And in this operator, you write code as in any other function and return a newly constructed instance by value. (Although, this looks a lot like a vexing parse) Tis is similar to executing the print function that was mentioned before.

So both would have different semantics and your compiler is right in failing to compile this code.

JVApen
  • 11,008
  • 5
  • 31
  • 67
1

In theory your hypothetical function apply would look quite simple as a template

#include <iostream>
#include <type_traits>
struct C
{
    C(int){};
};

template <class F, class... Args>
auto apply ( F&& f, Args&&... args) -> typename std::result_of<F(Args...)>::type
{
    return f(args...);
}

float foo (int a, float b) {  return a*b; }

int main()
{
    auto b = apply(&foo, 3, 5.f);
    //auto b = apply(&C::C, 3);  //error: taking address of constructor 'C::C'

    return 0;
}

But it is impossible to take address of constructor, as per the ISO standard. In most cases this special function is a non-entity in resulting code.

Neither explicit call to constructor is allowed. Your lambda function does thing differently. It constructs an object of class C as to per defined effect of that expression.

You could use a bogus argument and SFINAE

 template <class F, class... Args>
    auto apply ( F&& f, Args&&... args) 
        -> std::enable_if_t<std::is_invocable<F, Args...>::value,
           typename std::result_of<F(Args...)>::type>
{
    return f(args...);
}

template <class T, class... Args>
auto apply ( T&&, Args&&... args)
    -> std::enable_if_t<!std::is_invocable<T, Args...>::value,T&&>
{
    return std::move(T(args...));
}

but it's preferable to avoid that and rethink whatever meta-programming pattern you have in mind, i.e. avoid attempt to call same template name for different kinds of arguments, or make amends with lambda use. E.g. you may avoid to call apply() by using SFINAE.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • So there is no way to make a `apply` call to construct a non-default-constructable and non-copy-constructable class in C++11? – Midori Yakumo Sep 27 '19 at 07:24
  • i.e. C(1, 2, 3) vs apply(some-constructor-func, 1, 2, 3) where C is non-default-constructable and non-copy-constructable – Midori Yakumo Sep 27 '19 at 07:26
  • @MidoriYakumo should be able to create movable one. And usually non-default, non-copyable classes are very special. Like singletons or unique authority holders (e.g. something like `unique_ptr` or `boost::scoped_ptr`) – Swift - Friday Pie Sep 27 '19 at 08:27
  • @MidoriYakumo your C is copyable. It's not movable though. To not be copyable it have to have: `C(const C&) = delete;` or `C(C&& o)` explicitly declared. It should be either movable or copyable or assignments above won't work. – Swift - Friday Pie Sep 27 '19 at 09:12