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