如何在可变参数模板推导期间将指针隐式转换为 const 指针

How to implicitly convert a pointer, to a const pointer, during variadic template deduction

想象一下这样一种情况,其中一个人(出于某种原因或其他原因)想要实现一个函数,该函数在调用函数时充当代理。可以将其实现为一个模板,它接受一个指向函数的指针及其所有参数。但是,如果有一个函数接受指向某物的 const 指针(例如 int const*),而您正试图传递一个简单的指向函数的指针(例如 int*),则由于模板参数不明确,模板推导失败。示例:

#include <utility>
template <class... TArgs>
void foo (void (*pFunc)(TArgs...), TArgs... args)
    {
    pFunc (std::forward<TArgs> (args)...);
    }

void bar (int const* pInt) {}

int main ()
    {
    int a = 5;
    foo (bar, &a);
    }

产生:

error C2672: 'foo': no matching overloaded function found

error C2782: 'void foo(void (__cdecl *)(TArgs...),TArgs...)': template parameter 'TArgs' is ambiguous

note: see declaration of 'foo'

note: could be 'const int*'

note: or 'int*'

即使由于 int*int const*.

之间的隐式转换,简单地调用 bar (&a) 也会成功

是的,我知道,可以直接指定参数类型(例如 (foo<int const*> (bar, &a)),但考虑到接受任意数量的参数这一事实,这样的列表会很长,而且,我个人认为会很难看。

另一种选择是,在需要这种转换的任何地方用 const_casts 来增加代码,但这也会以不希望的方式膨胀代码(或者换句话说,看起来也很丑)。

我能想到的第三个解决方案是提供 2 个参数包,如下所示:

template <class... TArgs, class... TArgs2>
void foo (void (*pFunc)(TArgs...), TArgs2... args)
    {
    pFunc (std::forward<TArgs2> (args)...);
    }

这将解决隐式参数转换的直接问题,但会引入另一个问题,在不匹配的情况下,编译器错误指向 foo 实现,而不是 foo 调用函数的参数,这将使我们更难确定在代码库中进行不匹配调用的确切位置。如果我使用上面的示例和以下 main 函数,我得到的错误示例:

int main ()
    {
    float b = 5;
    foo (bar, &b);
    }

error C2664: 'void (const int *)': cannot convert argument 1 from 'float *' to 'const int *'

note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

note: see reference to function template instantiation 'void foo<const int*,float*>(void (__cdecl *)(const int *),float *)' being compiled

所以,问题是,是否存在某种模板魔法,可以让它以我期望的方式工作,考虑到我上面给出的限制和注意事项?还是我的要求不合理?

您可以约束函数:

template <class... TArgs, class... TArgs2>
enable_if_t<is_invocable_v<void(TArgs...),TArgs2...>>
foo (void (*pFunc)(TArgs...), TArgs2... args)
    {
    pFunc (forward<TArgs2> (args)...);
    }