Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialization and lambda-type argument

I have such a utility class:

struct Atreturn
{
    std::function<void()> funcdestr;
    Atreturn( std::function<void()> fd ): funcdestr(fd) {}
    ~Atreturn() { funcdestr(); }
};

Note no explicit attribute at constructor.

Possible use should be:

  1. Direct-initializing constructor call:

    Atreturn hook ( [something]() { DestroySomething(something); } );
    
  2. Copy-initializing constructor call:

    Atreturn hook = [something]() { DestroySomething(something); };
    
  3. Direct-list-initializing constructor call:

    Atreturn hook { [something]() { DestroySomething(something); }};
    

Now the question: to my best knowledge, the method #1 and #2 should be allowed as they are theoretically the same thing, provided that there's no explicit at the constructor, while #3 should not be allowed because this syntax prevents conversions (at least it's so for int if you tried int{2.1}).

However, gcc 4.9 allows method #1 and #3, but not #2 (and says conversion from '...::<lambda()>' to non-scalar 'Atreturn' type requested). This sounds crazy because it normally happens only if you have explicit constructor. Can anybody explain, why?

Additionally, let me make this problem more explicit: I need some not too clumsy syntax to initialize this Atreturn object, at least without the need of extra braces or parentheses. The problem is that editors with auto-indent function have problems with proper reindentation when the argument is C++11 lambda. So I need some syntax that could be expressed as:

 Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); };
like image 414
Ethouris Avatar asked Mar 05 '15 10:03

Ethouris


People also ask

What is a lambda expression in C++?

In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it's invoked or passed as an argument to a function.

How do you pass a lambda function in C++?

Permalink. All the alternatives to passing a lambda by value actually capture a lambda's address, be it by const l-value reference, by non-const l-value reference, by universal reference, or by pointer.

Why do we need lambda expressions in C++?

One of the new features introduced in Modern C++ starting from C++11 is Lambda Expression. It is a convenient way to define an anonymous function object or functor. It is convenient because we can define it locally where we want to call it or pass it to a function as an argument.

What is the return type of lambda?

The return type for a lambda is specified using a C++ feature named 'trailing return type'. This specification is optional. Without the trailing return type, the return type of the underlying function is effectively 'auto', and it is deduced from the type of the expressions in the body's return statements.

How do you initialize a variable in a lambda function?

In C++14, you can introduce and initialize new variables in the capture clause, without the need to have those variables exist in the lambda function’s enclosing scope. The initialization can be expressed as any arbitrary expression; the type of the new variable is deduced from the type produced by the expression.

What are lambda expressions in Java 8?

Lambda Expressions are anonymous functions. These functions do not need a name or a class to be used. Lambda expressions are added in Java 8. Lambda expressions basically express instances of functional interfaces An interface with a single abstract method is called a functional interface. One example is java.lang.Runnable.

What is the lambda function in Python?

The Typescript lambda function has mainly been used and implanted in the typescript for reducing the script code lines. With the help of the (=>) arrow symbol, the lambda function is implemented in the typescript.

How to deduce the return type of a lambda expression?

The return type of a lambda expression is automatically deduced. You don't have to use the auto keyword unless you specify a trailing-return-type. The trailing-return-type resembles the return-type part of an ordinary method or function.


1 Answers

while #3 should not be allowed because this syntax prevents conversions (at least it's so for int if you tried int{2.1}).

That's not quite right. The rule is that narrowing conversions are not allowed. Other types of conversions are allowed. int{2.1} is a narrowing conversion, because it modifies the value, losing precision. int{2.0} is not a narrowing conversion, because the value is not changed.

The reason #2 fails is that it requires two implicit user-defined conversions, which is forbidden.

Conceptually, a copy-initialization such as:

Atreturn hook = []() {};

is equivalent to:

Atreturn hook = Atreturn([]() {});

(except that it cannot call 'explicit' constructors, and the compiler is allowed to elide the copy).

This means that first the lambda would have to implicitly convert to function<void()> and then that would have to implicitly convert to Atreturn. Both those conversions are a "user-defined conversion sequence" meaning they call a constructor, rather than built-in conversions like int to long, and the standard says that an implicit conversion sequence cannot include more than one user-defined conversion.

The problem is actually unrelated to lambdas, you can demonstrate exactly the same error like this:

struct L { };
struct F { F(L) { } };
struct A { A(F) { } };
A a = L();

l.cc:4:9: error: conversion from ‘L’ to non-scalar type ‘A’ requested
 A a = L();
         ^

Again, the problem is that the implicit conversion sequence L -> F -> A involves two user-defined conversions, which is forbidden.

I don't have much sympathy for your problem of wanting to adapt the code to help auto-indentation -- the code should not be mangled to suit a flawed editor. However, another option would be to add a template constructor which accepts anything that can be converted to std::function<void()> e.g.

struct Atreturn
{
  using func_type = std::function<void()>;
  template<typename T,
           typename Requires = decltype(func_type(std::declval<T&&>())>
    Atreturn(T t) : funcdestr(std::move(t)) { }
  ...
};

This will allow the lambda to be converted directly to Atreturn, without requiring an implicit conversion to function<void()> first.

like image 52
Jonathan Wakely Avatar answered Oct 22 '22 08:10

Jonathan Wakely