使用 lambda 和全局函数调用 ranges::for_each
Invoking ranges::for_each with lambda and global function
以下代码按预期工作:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, [](auto const & r){
foo(r);
});
但以下内容:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, &foo);
给出编译错误:
template argument deduction/substitution failed:
couldn't deduce template parameter 'F'
我看了一下 source 但老实说我无法理解这个问题。
如果不明确指定您想要的 overload/instantiation,您不能获取 overloaded 或 template 函数的地址。
在您的第一个代码段中传递 lambda 表达式 是解决此问题的好方法。
或者,如果您有权访问 foo
,您可以将其转换为 函数对象 :
namespace detail
{
struct foo_t
{
template <typename T>
void operator()(const T&) const { /* ... */ }
};
}
constexpr detail::foo_t foo{};
这允许你写:
ranges::for_each(rng, foo);
函数名不代表函数;是函数名的重载集
你的函数
void foo(auto const &){}
似乎正在使用语言的概念式扩展来实现简洁的模板功能。在标准 C++ 中,它将显示为:
template<class T>
void foo(T const &){}
模板函数不是函数。它是由该模板生成的一组重载函数。
函数名称的重载集不是 C++ 对象。可以传递给函数的是 C++ 对象。
现在,当一个函数名的重载集只命名一个函数时,编译器会自动解析重载并为您提供该函数对象。
当函数名称的重载集被转换为具有固定签名的指向函数的指针时,重载解析就会开始,并且(希望如此)选择一个。
然而调用for_each
时,参数不是一个特定的固定签名函数指针。相反,它是一个泛型类型参数。此时,编译器无法解析函数名的重载集。不清楚你要哪一个
有两种解决方法。其中之一是将您的函数重载集转换为特定的函数指针。这需要你是明确的。
第二个是将您的重载集顶部包装到一个对象中。您可以使用带有模板 operator()
的手动函数对象来执行此操作,或者在 C++14 中您可以执行此操作:
#define OVERLOADS_OF(...) \
[](auto&&...args) \
noexcept(noexcept(__VA_ARGS__(decltype(args)(args)...))) \
->decltype( __VA_ARGS__(decltype(args)(args)...) ) \
{ return __VA_ARGS__(decltype(args)(args)...); }
构建代表全局函数名称重载的无状态 lambda。
所以:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, OVERLOADS_OF(foo));
如果你想捕获更少的极端情况,一个简单的:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, [](auto&x){foo(x);});
也适用于这种特定情况。
顺便说一句,有一个 C++20 提议将 OVERLOADS_OF(foo)
替换为 [](auto&&...args)=>foo(decltype(args)(args)...)
(产生与宏相同的效果)。遗憾的是,decltype
和 noexcept
功能被否决了。
以下代码按预期工作:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, [](auto const & r){
foo(r);
});
但以下内容:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, &foo);
给出编译错误:
template argument deduction/substitution failed:
couldn't deduce template parameter 'F'
我看了一下 source 但老实说我无法理解这个问题。
如果不明确指定您想要的 overload/instantiation,您不能获取 overloaded 或 template 函数的地址。
在您的第一个代码段中传递 lambda 表达式 是解决此问题的好方法。
或者,如果您有权访问 foo
,您可以将其转换为 函数对象 :
namespace detail
{
struct foo_t
{
template <typename T>
void operator()(const T&) const { /* ... */ }
};
}
constexpr detail::foo_t foo{};
这允许你写:
ranges::for_each(rng, foo);
函数名不代表函数;是函数名的重载集
你的函数
void foo(auto const &){}
似乎正在使用语言的概念式扩展来实现简洁的模板功能。在标准 C++ 中,它将显示为:
template<class T>
void foo(T const &){}
模板函数不是函数。它是由该模板生成的一组重载函数。
函数名称的重载集不是 C++ 对象。可以传递给函数的是 C++ 对象。
现在,当一个函数名的重载集只命名一个函数时,编译器会自动解析重载并为您提供该函数对象。
当函数名称的重载集被转换为具有固定签名的指向函数的指针时,重载解析就会开始,并且(希望如此)选择一个。
然而调用for_each
时,参数不是一个特定的固定签名函数指针。相反,它是一个泛型类型参数。此时,编译器无法解析函数名的重载集。不清楚你要哪一个
有两种解决方法。其中之一是将您的函数重载集转换为特定的函数指针。这需要你是明确的。
第二个是将您的重载集顶部包装到一个对象中。您可以使用带有模板 operator()
的手动函数对象来执行此操作,或者在 C++14 中您可以执行此操作:
#define OVERLOADS_OF(...) \
[](auto&&...args) \
noexcept(noexcept(__VA_ARGS__(decltype(args)(args)...))) \
->decltype( __VA_ARGS__(decltype(args)(args)...) ) \
{ return __VA_ARGS__(decltype(args)(args)...); }
构建代表全局函数名称重载的无状态 lambda。
所以:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, OVERLOADS_OF(foo));
如果你想捕获更少的极端情况,一个简单的:
void foo(auto const &){}
auto const rng{ranges::view::all(v)};
ranges::for_each(rng, [](auto&x){foo(x);});
也适用于这种特定情况。
顺便说一句,有一个 C++20 提议将 OVERLOADS_OF(foo)
替换为 [](auto&&...args)=>foo(decltype(args)(args)...)
(产生与宏相同的效果)。遗憾的是,decltype
和 noexcept
功能被否决了。