这是通过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);
}
答案 0 :(得分:2)
您尝试做的事实上有两个问题:
要重载两个不同的实体,您需要一个帮助器类。幸运的是在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);}
};
答案 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);
};