Showing posts with label cpp_lambda. Show all posts
Showing posts with label cpp_lambda. Show all posts

Nov 25, 2025

[C++] lambda restrictions

Lambda Restrictions (as of c++26)

  • No implicit conversions, e.g. base class slicing
  • No member typedef
  • No compiler-selected overloading
  • No user-defined conversion functions
  • No user-defined implicit copy/move operations
  • No user-defined destructors
  • Limited template argument deduction
  • No directly extendable overload sets
  • No operator overloading
  • No array captures (at runtime. For compile time known size array, just like defined in struct, they are verbatimly copied/captured)
  • No separating declaration and definition
  • No linking across TUs (a lambda's type is effectively "private" to the file it is written in.)

Feb 10, 2022

[C++] ref data member is not const inside a const member function due to C++'s type system

Data member with reference type is not const inside a const member function due to C++'s type system.

i.e

c.v is decorated from right to left thus

int& const data_member;

is not valid.

struct Fun {
  int &a;
  void run() const { a += 1; }
};

int main() {

  int a = 42;
  [&a] { a += 1; }();

  Fun f{a};
  f.run();
}

Jan 13, 2016

[C++] Lambda issues

https://www.reddit.com/r/cpp/comments/40lm8o/lambdas_are_dangerous/
https://www.reddit.com/r/cpp/comments/40scxe/jrbprogramming_a_workaround_for_lambda_odr/

A potential code bloat could be caused by using lambda for template arguments.

However, there's another issue, violating ODR.

Read on the reference, jot down result later...

--------------
Update:
Code taken from: A Workaround for Lambda ODR Violations
 // Based on Richard Smith trick for constexpr lambda
    // via Paul Fultz II (http://pfultz2.com/blog/2014/09/02/static-lambda/)
    template<typename T>
    auto addr(T &&t)
    {
        return &t;
    }

    static const constexpr auto odr_helper = true ? nullptr : addr([](){});

    template <class T = decltype(odr_helper)>
    inline void g() {
        int arr[2] = {};
        std::for_each(arr, arr+2, [] (int i) {std::cout << i << ' ';});
    }


It's still an ODR violation, which is that,
any inline function in the header spread into different TU that
calls g() will have different definition, due to any lambda expression
in a single TU has a different type.


Reference:

Dec 17, 2014

[C++14] lambda expression type extract tricks

From Paul Fultz II's article:
Static Lambdas: Initializing lambdas at compile time

Interesting tricks that he had applied.

Instead of initializing lambda object at program start-up.

Idea:
1.
lambda expression object is generated during run-time;
however, the type itself for sure is completed during compile time.

2.
Extract the lambda expression type thus could be used for constexpr variable. 


Steps:

1.
Extract ptr to lambda expression type.
Of course, use template function to deduce :-)
this function is not constexpr due to it's parameter isn't literal type.
--
template<typename T>
typename std::remove_reference<T>::type* GetPtrToLambda(T&& t)
{
    return &t;
}

2.
A constexpr template function could extract the type: Wrapper<T> explained later.
--
template<typename T>
constexpr Wrapper<T> GetLambdaWrapper(T*)
{
    return {};
}

3.
GetPtrToLambda([](auto i){cout << i << endl;})
will be run on runtime; however, we only need the type at compile time. Trick Paul Fultz did:
--
true ? nullptr : GetPtrToLambda([](auto i){cout << i << endl;});
--
i.e, will get a ptr to lambda expression type but point to null.

4.
i.e:
--
>GetLambdaWrapper(true ? nullptr : GetPtrToLambda([](auto i){cout << i << endl;}));
--
Now we have the Wrapper<T> while T is the lambda expression type.

5.
Wrapper is straight forward: A stateless Wrapper of size 1 byte. T is the type of lambda expression type.
This works only for stateless lambda expression type.
--
template<typename T>
struct Wrapper
{
    template<typename... A>
    decltype(auto) operator()(A&&... a) const
    {
        return reinterpret_cast<const T&>(*this)(std::forward<A>(a)...);
    }
};

6.
The binary size of the code is roughly the same as simply do:
auto lambda = [](auto i){cout << i << endl;};

7.
Full code:
----
#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
typename std::remove_reference<T>::type* GetPtrToLambda(T&& t)
{
    return &t;
}

template<typename T>
struct Wrapper
{
    template<typename... A>
    decltype(auto) operator()(A&&... a) const
    {
        return reinterpret_cast<const T&>(*this)(std::forward<A>(a)...);
    }
};

template<typename T>
constexpr Wrapper<T> GetLambdaWrapper(T*)
{
    return {};
}

constexpr auto lambda = GetLambdaWrapper(true ? nullptr : GetPtrToLambda(
    [](auto i){cout << i << endl;}));

int main()
{
    lambda(42);
}

Sep 25, 2014

[C++11] decltype(captured var) inside lambda expression

trap.

We would consider the lambda expression generated below:

void fun()
{
    static int a;

    int local_var = 42;

    [&local_var]{
        decltype(local_var) var_1 = 42;
    }();
}

equals to:

void fun()
{
  static int a;

  int local_var = 42;
  
  struct Lambda
  {
      int& local_var;

      Lambda(int& _local_var):local_var(_local_var){}

      void operator()() const
      {
          decltype(local_var) var_1 = 42; // won't compile
      }

  };
  
  Lambda{local_var}();
}
but not quite.

decltype(local_var) within the lambda expression is the type
of local variable local_var, which is int.

Local type Lambda's decltype(local_var)
is the type of l-value reference to int, which can't bind
to r-value 42.

While considering lambda expression
 'is equal to' type Lambda,
that's not exact the case for decltype(local_var) inside lambda expression.

decltype(local_var) is the type of outer scope's local_var,
i.e int.
(transformation not considered) 
reference: C++11 ISO14882-2011 , i.e : 5.1.2.18