auto and lambdas can be abused and overused just like many programming features, and when something is shiny and new, people are more likely to abuse them.
But don't let their abuses hide the fact that they are actually very useful.
They are especially handy for one-liner predicates to pass to algorithms. In such situations, either the one-liner is something you want to repeatedly use, and should be separated and "standardized" in your codebase (like std::less, for example, or my own case_insensitive_hash), or, if just used in a single place, shouldn't be seperated from the code that actually uses it.
Here's a real example from my code:
//Frees all the images whose shared-count has reached zero.
//Call this after loading an area, to free all the images that are no longer needed.
void AnimationRetriever::FreeAllUnused()
{
//Remove all the shared_ptrs that are shared by one or less sharers.
//Since the map's shared ptrs themselves count as a sharer, they are the 'one' sharecount.
//We keep the shared pointers in the map for precisely this reason - we want to leave an
//additional sharer keeping the object alive incase the next loaded area wants to use it.
MapRemoveValues(this->loadedAnimations, [](const AnimationSPtr &sPtr){ return (sPtr.use_count() <= 1); });
}
It wouldn't make sense to separate this lambda into its own function.
Now if I actually needed this behavior alot, I would make it its own function and pass in a parameter and a member func to call
(like if_mem_func_returns(&(std::shared_ptr::use_count), std::less_equal, 1) ), but since that's actually harder to read, I'd only do that if I found myself using this behavior alot.
Lambdas are also useful to clean up repetitive code from functions.
Here's a slightly messy real example:
(for context, the enclosing function is queuing for rendering the dotted rectangle around selections, with the squares on the corners and sides for stretching and resizing the selection)
//================================================
//Draw the grabpoints:
auto drawGrabPoint = [&](const cPointF ¢er, bool colorationButton)
{
using namespace Engine::Graphics;
cRectF positionRect = cRectF::CreateFromCenter(center, CornerGrabpointSize);
positionRect.Round();
const cRectF &OrangeSubrect = (colorationButton? CornerGrabpointSubRect_Orange_Coloration : CornerGrabpointSubRect_Orange);
const cRectF &BlueSubrect = (colorationButton? CornerGrabpointSubRect_Blue_Coloration : CornerGrabpointSubRect_Blue);
const cRectF &texCoords = (positionRect.Padded(1).Contains(this->lastMousePosInWorld) ? BlueSubrect //Hovered.
: this->stretching ? CornerGrabpointSubRect_Red //Skewing.
: OrangeSubrect); //Normal.
SetBasicTextureData(quadBatch, quadBatch.GetNextIndex(), positionRect, 0.0f, texCoords, this->guiTexture.GetDetails().textureSize, cColor::Full);
};
//Corners:
drawGrabPoint(decal.corners.topLeft, false);
drawGrabPoint(decal.corners.topRight, false);
drawGrabPoint(decal.corners.bottomRight, false);
drawGrabPoint(decal.corners.bottomLeft, false);
//Sides:
drawGrabPoint(cPointF::PointBetween(decal.corners.topLeft, decal.corners.topRight), false);
drawGrabPoint(cPointF::PointBetween(decal.corners.topRight, decal.corners.bottomRight), false);
drawGrabPoint(cPointF::PointBetween(decal.corners.bottomRight, decal.corners.bottomLeft), false);
drawGrabPoint(cPointF::PointBetween(decal.corners.bottomLeft, decal.corners.topLeft), false);
//================================================
(note that any mess is from my code within the lambda, not the usage of the lambda itself. The lambda makes the code clearer)
If the lambda was larger, I'd separate it into a standalone function called something like priv_drawGrabPoint(...), which would also have to take several additional parameters that the lambda was capturing for me.
Previously, I had to make every example of this time be a stand-alone "implementation" function, even when it was just a few lines of code, which unnecessarily separates related code from being right next to each other.
foreach( auto x : m_foos ) {}
Looks ok, right? Well turns out m_foos is std::vector<Foo> m_foos; The loop will perform a hard copy Foo in every iteration. [...snip...]
foreach( auto &x : m_foos ) { }
And now x is a reference, rather than a hardcopy.
But that's not an auto issue. That's a misuse of variables in-general issue.
If you did:
for( Foo x : m_foos )
...the exact same flaw would occur.
Further, I would suggest by default one should use const-reference, and only go to non-const reference or value (or r-value) is the code needs it (for example, if it's just integers).
If you use good coding practices, then "auto x : m_foos" stands out just as badly as "Foo x : m_foos".