(如何)我可以在一个地方推断模板类型参数和"只是使用"推断的类型在其他地方? &g



我的函数模板有一个函数指针(或成员函数指针)形参。函数指针可以接受任何实参,实参类型被推断出来。在形参列表的末尾,我希望函数模板采用与函数指针形参相同的形参。

template<typename R, typename... Args>
void f(R(*fp)(Args...), Args... args);

当参数类型不完全匹配时,问题就开始了,例如,假设fpBase*,f的调用者给你Derived*fpdouble,调用者输入0(类型为int)。

是否有任何方法告诉编译器仅基于fp推断Args和"只是使用"args的那些推断类型,而不是试图根据它们绑定到的参数匹配它们?

我找到了一个有效的解决方案。我不知道的术语是非演绎的语境。例如,作用域解析操作符::左边的所有内容都是非推导的。在c++ 20中,您可以使用std::type_identity_t来人为地创建这样的上下文,在c++ 14和c++ 17中,您可以创建自己的type_identity_t

所以你可以用

template<typename R, typename... Args>
void f(R(*fp)(Args...), std::type_identity_t<Args>... args);

关于完全转发,当fp通过引用或值接受参数时,f也会这样做。对于std::forward,按值形参将被移动,引用形参将被引用。

一种选择是为1推导不同的类型,并使用SFINAE或概念在以下情况下禁用过载:

// no SFINAE
template <typename R, typename... Args, typename... UArgs>
void f(R (*fp)(Args...), UArgs&&... args);
// SFINAE
template <typename R, typename... Args, typename... UArgs>
auto f(R (*fp)(Args...), UArgs&&... args)
-> std::void_t<std::invoke_result_t<decltype(fp), UArgs...>>;
// concepts
template <typename R, typename... Args, typename... UArgs>
auto f(R (*fp)(Args...), UArgs&&... args)
requires std::invocable<decltype(fp), UArgs...>;

此选项与std::type_identity选项的一个区别是,当您调用f时,需要将参数转换为fp,例如

struct X { X(int); };
void f1(X);
f(&f1, 33); // implicit conversion from int to X

std::type_identity的情况下,转换是在调用方端完成的(即,当您调用f时),而使用上面的选项,转换是在f内部调用fp时完成的。


1您也可以使用泛型函数类型,就像在评论中建议的那样,取决于您在f()中做什么。

相关内容