C++ 如何避免局部lambdas阴影重载解析?

C++ 如何避免局部lambdas阴影重载解析?,c++,lambda,c++14,C++,Lambda,C++14,这是通过4层间接寻址实现的精简版本。重叠分辨率在具有相同名称的本地lambda附近分解。这是一种维护性问题,特别是如果代码仍在生成(这里不是),并且错误只在测试中被捕获 有没有一种优雅的方法可以避免这种情况?有关此问题,请参阅 #include "catch.hpp" #include <iostream> #include <map> #include <string> namespace { struct Something {}; templat

这是通过4层间接寻址实现的精简版本。重叠分辨率在具有相同名称的本地lambda附近分解。这是一种维护性问题,特别是如果代码仍在生成(这里不是),并且错误只在测试中被捕获

有没有一种优雅的方法可以避免这种情况?有关此问题,请参阅

#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);
}
#包括“catch.hpp”
#包括
#包括
#包括
名称空间{
结构某物{};
模板
无效过程(常数T&x,常数s){}
结构A{
};
void进程(const&p,Something const&s){}
结构B{
};
void进程(B常数&p,某物常数&s){}
}//名称空间
int main{
结构C{};
//此lanbda会遮挡以前定义的视图的可见性
//具有相同名称的函数。这是一个可维护性问题
自动进程=[](C常量和p,某物常量和s){};
某物{};
A{};
B{};
C{};
过程(a,s);
过程(b,s);
}

实际上,您尝试执行的操作存在两个问题:

  • 范围阴影
  • 不能用现成的函数对象(lambda)重载函数
要重载两个不同的实体,需要一个助手类。幸运的是,在
C++17中,这真的很容易。
C++14
中所需的大量样板文件都是现成的:)


第一个lambda用于调用全局重载。
->decltype(::进程(p,s))
用于正确的SFNINAE。在您的情况下,这没有什么区别,但在更复杂的情况下,它会影响过载解决方案。

您可能会明确说明过载:

// 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...>;
简易解决方案:

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


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

您可以使用通用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);
               };
#包括
自动处理=[](常数自动和p,常数和s){
if/*constexpr*/(std::is_same::value){
//^^^^^^^^^^^^^^^^^^^^ C++17功能
/*做点什么*/
}
过程(p,s);
};

有没有一种优雅的方法可以避免这种情况?
重命名lambda?或者命名名称空间,以便在从它调用函数时可以使用它?lambda没有什么特别之处;所有的局部变量都是这样工作的,局部变量甚至没有什么特别之处。这是基本C++行为。名称位于作用域中,作用域可以嵌套,内部作用域中的名称在外部作用域中隐藏相同的名称,以便进行非限定名称查找。@UKMonkey:请注意,您建议的解决方案适用于这种简单情况。通常,尝试将新成员添加到重载集中。实际行为是替换重载集。您提出的解决方案根本不会改变重载集。答案很好,但这是下一个级别的东西。。。我很难掌握这些新的C++17结构的概念。它离优秀的旧C编程非常遥远。你能给我指出什么好的来源吗?@ JHBonarius,,JHBONALUS,如果你对C++充满激情,CppCon有很多很棒的视频,Bjarne本人(和Habor我想)有一些关于新功能的特别介绍。但是你会发现很多有趣的话题。谢谢。但是我已经知道了很多链接。我试着把我的脑袋绕在这个(我现在知道的是所谓的)模板元编程上……很有趣,但是代码是什么意思呢?所有的
都做什么。e、 g.
类…
Ts…
运算符()。我发现在没有任何解释的情况下阅读这篇文章是很困难的。那些
是用于可变模板的,所以你主要有
模板结构重载:T1,T2{使用T1::operator();使用T2::operator();}
但是对于任何数字,而不仅仅是2。@bolov很棒的链接。我不知道它叫那个,所以我不容易在CPP上找到它。我现在掌握了这个概念。让我们考虑一个应用程序来测试它……您的演示程序会抛出一个警告列表。@JHBonarius都是关于未使用的变量的。因为我们省略了函数体的细节,所以这些警告是可以接受的。
auto process = overloaded{
    [](C const& p, Something const& s) {},
    [](const auto& p, Something const& s){::process(p, s);}
};
::process(a, s);
::process(b, s);
#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);
               };