Initialization and Lambda Type Argument

I have a utility class like this:

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

Note that there is no explicit attribute in the constructor.

Possible use:

  • Call constructor with direct initialization:

     Atreturn hook ( [something]() { DestroySomething(something); } ); 
  • A constructor call with copy initialization:

     Atreturn hook = [something]() { DestroySomething(something); }; 
  • Constructor call with direct list initialization:

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

Now the question is: in my best knowledge, methods # 1 and # 2 should be allowed, since they are theoretically the same, provided that the constructor does not have explicit , and # 3 should not be allowed, because this syntax prevents conversions ( at least so for int if you tried int{2.1} ).

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

Also, let me make this problem more explicit: I need a not-too-clumsy syntax to initialize this Atreturn object, at least without extra brackets or parentheses. The problem is that editors with auto index function have problems with proper reindexing when the argument is C ++ 11 lambda. Therefore, I need a syntax that can be expressed as:

  Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); }; 
+6
source share
1 answer

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

This is not entirely correct. The rule is that narrowing conversions is prohibited. Other conversion types allowed. int{2.1} is a narrowing transformation because it changes the value, losing accuracy. int{2.0} not a narrowing conversion because the value does not change.

The reason # 2 is not that it requires two implicit user conversions, which is prohibited.

Conceptually, 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 copy 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 of these transformations are a β€œuser-defined transform sequence”, meaning that they invoke the constructor instead of built-in transforms such as int - long , and the standard states that an implicit transform sequence cannot contain more than one user-defined transform.

The problem is actually not related to lambdas, you can demonstrate exactly the same error as 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 includes two user conversions that are forbidden.

I don’t have much sympathy for your problem related to the need to adapt the code to help indent automatically - the code should not be distorted so that it distorts the editor. However, another option is to add a template constructor that accepts anything that can be converted to std::function<void()> for example.

 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 convert the lambda directly to Atreturn , without first requiring an implicit conversion to function<void()> .

+7
source

Source: https://habr.com/ru/post/983364/


All Articles