Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Templated lambdas in C++17 without an auto argument

I have a class Derived that inherits from class Base<ResourceType>:

template <class ResourceType>
class Base {
protected:
  ResourceType* resource;
public:
  void set_resource(ResourceType* resource) {
    this->resource = resource;
  }
};

template <class ResourceType>
class Derived : public Base<ResourceType> {
public:
  using Base<ResourceType>::resource;
  void print () {
    std::cout << *resource << std::endl;
  }
};

I want to create a factory that creates objects of type Derived. I can of course do this with functions:

template <typename ResourceType>
auto derived_factory () { 
  return new Derived<ResourceType>(); 
}

auto derived = *(derived_factory<int>());

But, I am not able to write a lambda function for the factory. I can write templated lambda functions if I was accepting a template argument using the auto keyword, but here I just want to use the template to determine the return type. The following fails:

auto derived_factory = []<typename ResourceType>() {
  return new Derived<ResourceType>();
};

auto derived = *(derived_factory<int>());

with the error:

inherit_unknown_type.cpp: In function ‘int main()’:
inherit_unknown_type.cpp:27:36: error: expected primary-expression before ‘int’
   auto derived = *(derived_factory<int>());
                                    ^~~
inherit_unknown_type.cpp:27:36: error: expected ‘)’ before ‘int’

Am I just calling the lambda incorrectly? Or do I have to wait for C++20?

like image 216
Sagar Jha Avatar asked Dec 10 '22 06:12

Sagar Jha


2 Answers

Template parameter lists in lambda expressions is a C++20 feature.

(In fact, my GCC says that in the diagnostic: error: lambda templates are only available with -std=c++2a or -std=gnu++2a [-Wpedantic])

But you don't have to wait for C++20, it's already supported by GCC 8 with -std=c++2a flag.

And you'll have to change the call syntax: Instead of derived_factory<int>(), you need derived_factory.operator()<int>().


As an alternative (if you don't want a free function), I suggest using a variation of tag dispatch:

auto derived_factory = [](auto tag) {
    return new Derived<typename tag::type>();
};

template <typename T> struct tag_type {using type = T;};

// Usage:
derived_factory(tag_type<int>{})

Also, even if you make it compile somehow, this line:

auto derived = *(derived_factory<int>());

will cause a memory leak no matter what. To avoid that, you should store the result as a pointer or a reference. Or even better, use a smart pointer.

like image 109
HolyBlackCat Avatar answered Jan 06 '23 12:01

HolyBlackCat


Waiting for C++20, you can return the lambda from a template class

template <typename ResourceType>
auto make_derived_factory ()
 { return []{ return new Derived<ResourceType>{}; }; }

auto derived = make_derived_factory<int>();

int main ()
 {
   auto df { derived() };
 }
like image 35
max66 Avatar answered Jan 06 '23 12:01

max66