Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does it mean to inherit from lambda?

Tags:

c++

Found this code which looks to be interesting:

auto a = [](){};

class B : decltype(a)
{
};

I want to know what it does. Can this be useful in any way?

like image 713
user6511134 Avatar asked Jun 25 '16 01:06

user6511134


People also ask

What does it mean to Lambda capture this?

The lambda is capturing an outside variable. A lambda is a syntax for creating a class. Capturing a variable means that variable is passed to the constructor for that class. A lambda can specify whether it's passed by reference or by value.

How do you explain lambda?

00:05 A lambda function is a simple, short, throwaway function which is designed to be created inline in code. They're also known as lambda expressions, anonymous functions, lambda abstractions, lambda form, or function literals.

What is special about Lambda?

AWS Lambda extensions enable easy integration with your favorite monitoring, observability, security, and governance tools. Lambda invokes your function in an execution environment, which provides a secure and isolated runtime where your function code is executed.

What are lambdas in java?

A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.


Video Answer


3 Answers

Well, that code will compile, but the problem is that you will be unable to default construct any object of that class1, because the constructor of the lambda isn't accessible (other than copy/move constructors). The only constructors guaranteed by a lambda type is a defaulted copy/move constructor. And there's no default constructor

[expr.prim.lambda/21]

The closure type associated with a lambda-expression has no default constructor and a deleted copy assignment operator. It has a defaulted copy constructor and a defaulted move constructor ([class.copy]). [ Note: These special member functions are implicitly defined as usual, and might therefore be defined as deleted. — end note ]

or from cppreference:

//ClosureType() = delete;                     //(until C++14)
ClosureType(const ClosureType& ) = default;   //(since C++14)
ClosureType(ClosureType&& ) = default;        //(since C++14)

The history of the lambda constructor being inaccessible dates back to its early days proposal, found here

In section 3, second paragraph, and I quote:

In this translation, __some_unique_name is a new name, not used elsewhere in the program in a way that would cause conflicts with its use as a closure type. This name, and the constructor for the class, do not need to be exposed to the user—the only features that the user can rely on in the closure type are a copy constructor (and a move constructor if that proposal is approved) and the function call operator. Closure types do not need default constructors, assignment operators, or any other means of access beyond function calls. It may be worthwhile for implementability to forbid creating derived classes from closure types. ...

As you can see, the proposal even suggested that creating derived classes from closure types should be forbidden.


1 of course you can copy-initialize the base class with a in order to initialize an object of type B. See this


Now, to your question:

Can this be useful in any way?

Not in your exact form. Your's will only be instantiable with the instance a. However, if you inherit from a generic Callable Class such as a lambda type, there are two cases I can think of.

  1. Create a Functor that calls a group of functors in a given inheritance sequence:

    A simplified example:

    template<typename TFirst, typename... TRemaining>
    class FunctionSequence : public TFirst, FunctionSequence<TRemaining...>
    {
        public:
        FunctionSequence(TFirst first, TRemaining... remaining)
            : TFirst(first), FunctionSequence<TRemaining...>(remaining...)
        {}
    
        template<typename... Args>
        decltype(auto) operator () (Args&&... args){
            return FunctionSequence<TRemaining...>::operator()
                (    TFirst::operator()(std::forward<Arg>(args)...)     );
        }
    };
    
    template<typename T>
    class FunctionSequence<T> : public T
    {
        public:
        FunctionSequence(T t) : T(t) {}
    
        using T::operator();
    };
    
    
    template<typename... T>
    auto make_functionSequence(T... t){
        return FunctionSequence<T...>(t...);
    }
    

    example usage:

    int main(){
    
        //note: these lambda functions are bug ridden. Its just for simplicity here.
        //For correct version, see the one on coliru, read on.
        auto trimLeft = [](std::string& str) -> std::string& { str.erase(0, str.find_first_not_of(' ')); return str; };
        auto trimRight = [](std::string& str) -> std::string& { str.erase(str.find_last_not_of(' ')+1); return str; };
        auto capitalize = [](std::string& str) -> std::string& { for(auto& x : str) x = std::toupper(x); return str; };
    
        auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize);
        std::string str = " what a Hullabaloo     ";
    
        std::cout << "Before TrimAndCapitalize: str = \"" << str << "\"\n";
        trimAndCapitalize(str);
        std::cout << "After TrimAndCapitalize:  str = \"" << str << "\"\n";
    
        return 0;
    }
    

    output

    Before TrimAndCapitalize: str = " what a Hullabaloo     "
    After TrimAndCapitalize:  str = "WHAT A HULLABALOO"
    

    See it Live on Coliru

  2. Create a Functor with an overloaded operator()(...), overloaded with all base classes' operator()(...):

    • Nir Friedman has already given a good instance of that in his answer to this question.
    • I have also drafted out a similar and simplified example, drawn from His. See it on Coliru
    • Jason Lucas also demonstrated its practical applications in his CppCon 2014 presentation "Polymorphism with Unions". You can find the Repo here, one of exact location in source code here (Thanks Cameron DaCamara)

Another cool trick: Since the resulting type from make_functionSequence(...) is a callable class. You can append more lambda's or callable to it at a later time.

    //.... As previously seen

    auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize);

    auto replace = [](std::string& str) -> std::string& { str.replace(0, 4, "Whaaaaat"); return str; };

    //Add more Functors/lambdas to the original trimAndCapitalize
    auto replaced = make_functionSequence(trimAndCapitalize, replace /*, ... */);
    replaced(str2);
like image 61
WhiZTiM Avatar answered Oct 09 '22 09:10

WhiZTiM


Lambdas are function objects underneath, with additional syntatic sugar. a evaluates to something like that (with the MyLambda name being a random name, just like when you make namespace {} - namespace name will be random):

class MyLambda {
public:
    void operator()() {
    }
}

So when you inherit from a lambda, what you're doing is inheriting from an anonymous class / structure.

As for usefulness, well, it's as useful as any other inheritance. You can have the functionality of multiple lambdas with multiple inheritance in one object, you can add new methods to it to extend it. I can't think of any real application at the moment, but I'm sure there are many.

Refer to this question for more information.

like image 8
Jezor Avatar answered Oct 09 '22 08:10

Jezor


This can actually be quite useful, but it depends how direct you want to be about the whole thing. Consider the following code:

#include <boost/variant.hpp>

#include <iostream>
#include <string>
#include <unordered_map>

template <class R, class T, class ... Ts>
struct Inheritor : public  T, Inheritor<R, Ts...>
{
  using T::operator();
  using Inheritor<R, Ts...>::operator();
  Inheritor(T t, Ts ... ts) : T(t), Inheritor<R, Ts...>(ts...) {}
};

template <class R, class T>
struct Inheritor<R, T> : public boost::static_visitor<R>, T
{
  using T::operator();
  Inheritor(T t) : T(t) {}
};

template <class R, class V, class ... T>
auto apply_visitor_inline(V& v, T ... t)
{
  Inheritor<R, T...> i(t...);
  return boost::apply_visitor(i, v);
}

int main()
{
  boost::variant< int, std::string > u("hello world");
  boost::variant< int, std::string > u2(5);

  auto result = apply_visitor_inline<int64_t>(u, [] (int i) { return i;}, [] (const std::string& s) { return s.size();});
  auto result2 = apply_visitor_inline<int64_t>(u2, [] (int i) { return i;}, [] (const std::string& s) { return s.size();});
  std::cout << result;
  std::cout << result2;
}

The snippet in your question does not show up in exact form anywhere. But you can see that the types of lambdas are being inferred in apply_visitor_inline. A class is then instantiated that inherits from all of these lambdas. The purpose? We are able to combine multiple lambdas into a single one, for the purpose of things like apply_visitor. This function expects to receive a single function object that defines multiple operator() and distinguish between them based on overloading. But sometimes it's more convenient to define a lambda that operates on each of the types we have to cover. In that case, inheritance from lambdas provides a mechanism for combining.

I got the inline visitor idea from here: https://github.com/exclipy/inline_variant_visitor, though I did not look at the implementation there, so this implementation is my own (but I guess its very similar).

Edit: the originally posted code only worked due to a bug in clang. According to this question (Overloaded lambdas in C++ and differences between clang and gcc), the lookup for multiple operator() in base classes is ambiguous, and indeed the code I posted originally did not compile in gcc. The new code compiles in both and should be compliant. Sadly, there is no way seemingly to do a variadic using statement, so recursion must be used.

like image 6
Nir Friedman Avatar answered Oct 09 '22 07:10

Nir Friedman