如何在可变参数模板推导期间将指针隐式转换为 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_cast
s 来增加代码,但这也会以不希望的方式膨胀代码(或者换句话说,看起来也很丑)。
我能想到的第三个解决方案是提供 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)...);
}
想象一下这样一种情况,其中一个人(出于某种原因或其他原因)想要实现一个函数,该函数在调用函数时充当代理。可以将其实现为一个模板,它接受一个指向函数的指针及其所有参数。但是,如果有一个函数接受指向某物的 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 founderror C2782: '
void foo(void (__cdecl *)(TArgs...),TArgs...)
': template parameter 'TArgs
' is ambiguousnote: see declaration of '
foo
'note: could be '
const int*
'note: or '
int*
'
即使由于 int*
和 int const*
.
bar (&a)
也会成功
是的,我知道,可以直接指定参数类型(例如 (foo<int const*> (bar, &a)
),但考虑到接受任意数量的参数这一事实,这样的列表会很长,而且,我个人认为会很难看。
另一种选择是,在需要这种转换的任何地方用 const_cast
s 来增加代码,但这也会以不希望的方式膨胀代码(或者换句话说,看起来也很丑)。
我能想到的第三个解决方案是提供 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)...);
}