Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a non-captured variable be shadowed by a lambda parameter?

I have a code that looks like this - it's heavily simplified but this snippet compiles and exhibits the same behaviour:

template <typename TFunc>
float FloatSelect( const float in_value, TFunc&& Predicate) {
  return std::forward<TFunc>(Predicate)(in_value) ? in_value : 0.0f;
};

void DisplayFloatSelect() {
  const float value = FloatSelect(
    -1.0f,
    [] (const float value) { return value > 0.0f; }
  );

  std::cout << value << std::endl;
}

With -Wshadow enabled the compiler emits the following warning (as seen here):

12 : warning: declaration shadows a local variable [-Wshadow]

[] (const float value) { return value > 0.0f; }

^

10 : note: previous declaration is here

const float value = FloatSelect(

^

Which is not really helpful - I understand what shadowing a variable is about but as the lambda does not capture anything, it should be fine here.

What am I missing?

like image 334
Gama Avatar asked Jul 13 '15 18:07

Gama


People also ask

Is it possible to access variables from the outside of a lambda expression?

The rule is that a lambda expression can only access local variables from an enclosing scope that are effectively final.

Can lambda function access local variables?

Local variables from outer scope can be captured inside Lambda in 2 modes i.e.

How can a global variable be shadowed?

The global variable a is said to be shadowed by the local variable a.

What is shadowing a parameter?

Declaring a variable with a name that already refers to another variable is called shadowing. In this case, you shadow a function argument.


2 Answers

Yes, a non-captured variable can be shadowed by a lambda parameter.

In the particular case of the lambda in the OP, you might argue that the inner declaration of value does not shadow the scope of the outer declaration because the lambda has no captures. Nonetheless, the outer value can be seen inside the body of the lambda, because the body of the lambda is still inside the scope of the enclosing block:

(C++14 §5.1.2/p.7): The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator, but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming idexpressions referring to non-static class members into class member access expressions using (*this) (9.3.1), the compound-statement is considered in the context of the lambda-expression.

The odr-use of a non-captured variable from the outer scope is an error, but it is possible for a lambda without captures to make use of a name defined in an outer scope if it is not an odr-use (and in such a case, the variable is not captured.) In particular, it is possible to use const variables from an outer scope:

const int i = 20;
int f = ([](){return i + 3;})();

So even though the lambda has no captures, an explicit argument named i would certainly shadow the outer i. (See http://coliru.stacked-crooked.com/a/006f5f20cca841d5; you might want to try enabling -Wshadow.)

Since -Wshadow is precisely intended to reveal this sort of ambiguous name usage, it doesn't seem too surprising that it triggers a warning in the case in the OP.

-Wshadow is not enabled by either -Wall nor by -Wextra precisely because it is often going to warn you about something you don't really care about.

like image 151
rici Avatar answered Oct 18 '22 10:10

rici


You're missing that compilers are not perfect and sometimes emit silly warnings for relatively new language features in circumstances where they don't make much sense.

On the other hand, warnings are often all about ensuring that you really meant to write the code you wrote, instead of some other code. Perhaps you meant to capture the outer value but — for whatever reason: perhaps you weren't paying attention — failed to capture it and redeclared it instead.

In this code that does seem like a bit of a stretch, but the point is that warnings don't always tell you about the code you wrote: they sometimes tell you about the code the compiler thinks you possibly meant to write.

And then it's pretty trivial to use a different name for this parameter to say "hey, yeah, I know".

That would also make for much clearer code with a well-documented intent.

like image 20
Lightness Races in Orbit Avatar answered Oct 18 '22 08:10

Lightness Races in Orbit