7

I'm dealing with some C code that takes some data, and forwards it to the function passed in:

void foo(int* data, void (*fun)(int*)){
  (*fun)(data);
};

The following works without warning:

void bar(int* data){};

int main(){
  int data=0;
  foo(&data,bar);
}

However, if I use a lambda instead:

int main(){

  int data=0;
  foo(&data,[](auto data){});
}

I get the following warning:

warning: declaration of ‘data’ shadows a previous local [-Wshadow]
   foo(&data,[](auto data){});
                         ^
o.cpp:14:7: note: shadowed declaration is here
   int data=0;

But I thought an empty capture group would exclude the first instantiation during its look up.

Is this warning legitimate?
Why isn't the empty capture enough to avoid warnings?

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • I'd think the warning is only there to warn you that you may have thought you were using `data` and not `data`. It doesn't know what your *intent* was. – kmdreko May 24 '16 at 03:56
  • 3
    The _name_ is indeed being shadowed. You don't need to capture a symbol in order to use its name, e.g. in an unevaluated context such as `decltype`. – ildjarn May 24 '16 at 03:59
  • At least in Visual Studio 2015 you *cannot* use symbols in an unevaluated context (even decltype(data) or sizeof(data) fail with an `error C2065: 'data' undeclared identifier`) – Andreas H. May 24 '16 at 05:27
  • @AndreasH. see [expr.prim]/7 , "[...] for purposes of name lookup, [...] the *compound-statement* is considered in the context of the *lambda-expression*". It seems like your compiler is failing at name lookup, which is separate to whether or not captures occur. – M.M May 24 '16 at 05:36

2 Answers2

4

Names from the enclosing scope of the lambda are also in the scope of the lambda.

Names that are not captured may still be used, so long as they are not odr-used. Only odr-used variables must be captured. Example:

#include <iostream>

template<typename T> void foo(const int *, T f) { std::cout << f(5) << '\n'; }

int main()
{
    const int data=0;
    foo(&data,[](int baz){
        return data;
    });
}

Because reading a constant expression is not odr-use, this code is correct and data refers to the variable in main.

This program outputs 0, but if you change int baz to int data, it outputs 5.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • An empty capture list does not capture any variable from the current scope (see here http://en.cppreference.com/w/cpp/language/lambda). Visual Studio 2015 does not compile your example and fails with error C3493 (no implicit capture of 'data' if not default capture type is given) – Andreas H. May 24 '16 at 05:02
  • @AndreasH The point of my answer is that those names are in scope and may be used in limited ways even though they are not captured – M.M May 24 '16 at 05:03
  • This would mean that I could write [](int baz) { return sizeof(data); } because this is no odr-use of data (so no capture necessary) and it should still be in the scope of the lambda - or am I missing something? – Andreas H. May 24 '16 at 05:14
  • Yes you can write that – M.M May 24 '16 at 05:20
  • On the page you linked, search for `const int x` - there is an example very similar to mine . Perhaps MSVC is bugged – M.M May 24 '16 at 05:22
  • Ok. This is definetly not supported by Visual Studio 2015. Even the example from cppreference.com gives me error C3493. – Andreas H. May 24 '16 at 05:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112746/discussion-between-andreas-h-and-m-m). – Andreas H. May 24 '16 at 07:17
0

In reference to MISRA C++ 2008: An identifier declared in an inner scope should never have the same name as an identifier declared in the outer scope.

In your example int data is declared in the outer scope but goes to the inner scope of your lambda via reference. The problem is that you also have a parameter in the parameter list of your lambda called data(inner scope). This leads to a hiding of the data variable from the outer scope within the lambda.

By the way. Also your first example with function pointer should be rewritten because there is also a conflict with naming’s of identifiers in inner and outer scope. In this case it is not really bad cause effective there is only one data variable in use within the inner score. However, when parameter list variables and variables from the outer scope which calls the function have the same name this could lead to programmers confusion and should be also avoid.

cpow
  • 476
  • 4
  • 8