如何避免局部lambdas阴影重载决议?

时间:2018-02-28 10:39:41

标签: c++ lambda c++14

这是通过4层间接发生的事情的简化版本。重叠分辨率在具有相同名称的本地lambda附近分解。这是某种维护问题,特别是如果代码仍然构建(这里没有)并且错误仅在测试中被捕获。

有没有一种优雅的方法来规避这个?有关此问题,请参阅Godbolt Playgound

#include "catch.hpp"

#include <iostream>
#include <map>
#include <string>

namespace {

struct Something {};

template <typename T>
void process(const T& x, Something const& s){}

struct A {
};

void process(A const& p, Something const& s){}

struct B {
};

void process(B const& p, Something const& s){}
} // namespace

int main {
    struct C {};

    // THIS lanbda shadows the visibility of formerly defined
    // functions with same name. This is a maintainability issue
    auto process = [](C const& p, Something const& s) {};

    Something s{};

    A a{};
    B b{};
    C c{};

    process(a, s);
    process(b, s);
}

4 个答案:

答案 0 :(得分:2)

您尝试做的事实上有两个问题:

  • 范围阴影
  • 您无法使用开箱即用的函数对象(lambda)重载函数。

要重载两个不同的实体,您需要一个帮助器类。幸运的是在C++17中,这很容易。 C++14中需要的许多样板都在窗外:)

namespace util
{
template <class... Fs>
struct Overload : public Fs...
{
    using Fs::operator()...;
};

template <class... Fs> Overload(Fs...) -> Overload<Fs...>;
}

现在在你的主要:

auto process = util::Overload {
    [] (const auto& p, Something const& s) -> decltype(::process(p, s)) {
                 return ::process(p, s);
    },
    [](C const& p, Something const& s) { }
};

第一个lambda用于调用全局重载。 -> decltype(::process(p, s))适用于SFNINAE。在你的情况下,它没有什么区别,但在更复杂的情况下,它将影响重载分辨率。

答案 1 :(得分:2)

您可能明确了解过载:

// C++17 implementation, might have similar code for C++11/C++14
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

然后:

auto process = overloaded{
    [](C const& p, Something const& s) {},
    [](const auto& p, Something const& s){::process(p, s);}
};

Demo

答案 2 :(得分:1)

简易解决方案:

::process(a, s);
::process(b, s);

即使这些重载存在于匿名命名空间中,也可以通过在周围(全局)命名空间中查找来找到它们。

您的解决方案使用非限定名称查找。非限定名称查找在本地范围内开始,因此它会找到一个候选process

答案 3 :(得分:0)

您可以使用通用lambda,以便所有调用都调用此lambda,并根据lambda正文中参数的类型进行处理。

#include <type_traits>

auto process = [](const auto& p, Something const& s) {
                   if /* constexpr */ (std::is_same<std::decay_t<decltype(p)>, C>::value) {
                   // ^^^^^^^^^^^^^^^ C++17 feature
                       /* do something */
                   }
                   else ::process(p, s);
               };

Demo