1

以下代码按预期工作:

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'

我查看了源代码,但老实说,我无法理解问题所在。

4

2 回答 2

3

如果不明确指定所需的重载/实例化,则无法获取重载模板函数的地址。

在您的第一个代码段中传递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);
于 2017-03-31T13:04:01.307 回答
3

函数名不代表函数;它是函数名的重载集。

你的功能

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)...)(产生与宏相同的效果)。可悲的是,decltypenoexcept功能被否决了。

于 2017-03-31T13:15:29.087 回答