2

I can compile the following code without any problem (using gcc 11.1.0):

#include <iostream>

template <typename Func>
class K {
    Func f;
public:
    K(Func _f): f{_f} {};
    void do_thing(int x) {f(x);};
};

int main()
{
    auto f = [](int x) {std::cout << x << std::endl;};
    K kl{f};
    kl.do_thing(5);

    return 0;
}

however I would like to perform some check in the constructor of the class K (for instance some std::is_convertible_v inside some bool function), so I tried to modify the code to

#include <iostream>

template <typename Func>
class K {
    Func f;
    
public:
    K(Func _f) {
    ...
    f = _f;};
    void do_thing(int x) {f(x);};
};

int main()
{
    auto f = [](int x) {std::cout << x << std::endl;};
    K kl{f};
    kl.do_thing(5);

    return 0;
}

which however gives me some error message

error: use of deleted function ‘main()::<lambda(int)>::<lambda>()’

and then

note: a lambda closure type has a deleted default constructor

This confuses me a lot since I cannot understand how it is possible that the former piece of code could compile since the lambda function has not default constructor.

Question
How can I set my f inside the body of the constructor? (This is just a MWE and in my case the class is a bit more complex and the checks I mentioned before make sense.)

MaPo
  • 613
  • 4
  • 9
  • 1
    you cannot initialize the member in the body of the constructor. It is initialized before the body of the constructor is executed. – 463035818_is_not_an_ai Sep 08 '21 at 10:49
  • I see, so how can I set the f to be the same as _f inside the body, after the checks I have to do? – MaPo Sep 08 '21 at 10:51
  • I dont quite understand your motivation. What kind of checks can you do with the second version of the code that you cannot do with the first? – 463035818_is_not_an_ai Sep 08 '21 at 10:51
  • I want to check for signature of that function and if the signature is not what I expect I set `f` to be some default function (according to some other parameters I have in the class. Here those parameters do not appear since is a MWE). – MaPo Sep 08 '21 at 10:53
  • 2
    sounds like a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Your complete code will have more issues because each lamda expression has a unique type. Maybe ask for the actual problem you are trying to solve, but consider that this question already has an answer, so perhaps open a new one – 463035818_is_not_an_ai Sep 08 '21 at 10:54
  • 1
    you could use SFINAE to check the right signature, then initializing the lambda in the initializer list is fine. – 463035818_is_not_an_ai Sep 08 '21 at 10:56
  • As side note: `f = _f;` is super confusing; Normally class fields have names with underscore, not method arguments. – pptaszni Sep 08 '21 at 12:03
  • "_I would like to perform some check in the constructor of the class `K`_" - Should this check have an effect on the initialization of `f`? If so, can you describe a little bit about what you want? – Ted Lyngmo Sep 08 '21 at 15:01
  • To put my question in other words: Why not initialize it in the member initializer list? – Ted Lyngmo Sep 08 '21 at 18:21

2 Answers2

5

How can I initialize my f inside the body of the constructor?

You can't. f = _f; inside the constructor body is assignment but not initialization. So f will be default-initialized firstly, then enter the constructor body (to perform assignment).

You might use std::function instead; which could be default-initialized, then you can assign it in constructor body.


BTW: Since C++20 your code will compile fine even it might not work as you expected (depending on the ... part). For lambdas,

If no captures are specified, the closure type has a defaulted default constructor. Otherwise, it has no default constructor (this includes the case when there is a capture-default, even if it does not actually capture anything).

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
0

Is this what you are looking for? It is the generic solution, not making any assumptions on the signature of Fn

#include <iostream>
#include <utility>

template <typename Fn>
class K
{
public:
    explicit K(const Fn& fn) :
        m_fn{ fn }
    {
    };

    template<typename... args_t>
    auto do_thing(args_t&&... args)
    {
        return m_fn(std::forward<args_t>(args)...);
    }

private:
    Fn m_fn;

};

int main()
{
    auto f = [](int x) {std::cout << x << std::endl; };
    K kl{ f };
    kl.do_thing(5);
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19