有没有一种方法可以推断出函数指针模板参数的值?

7

C++允许非类型模板参数是指针,包括函数指针、类型等。我最近在这里问了一个关于其有何用处的问题,并且这是对其中一个答案的跟进。

如果给定一个作为函数指针的函数参数,是否可以推断出函数指针模板参数的值?例如:

using VoidFunction = void(*)();

template <VoidFunction F>
void templ(VoidFunction);

...

void func();  // a VoidFunction

...

templ<func>(func);  // works, but I have to specify the template parameter explicitly
templ(func);        //  <-- I would like to be able to do this

有没有办法让这个推导发生呢?从编译器实现者的角度来看,只要函数参数可以在编译时解析为代码中的函数,技术上似乎是可行的。
如果你想知道背后的动机,请查看此答案下面的评论,特别是std::bind()实现的可能优化。
编辑:我意识到我可以简单地删除函数参数并使用模板参数,如templ<func>()。我添加函数参数的唯一目的是尝试避免传递模板参数。
我想要的是,推断出函数指针的类型,例如:
template <typename Function, Function F>
void templ(/* something */);

接着就可以调用

templ(func);

或者

templ<func>();

并且从函数指针的单个提及中推断类型和值。

希望现在更容易理解了。


它该如何推断呢?例如,我可以这样做:template<std::size_t N> void foo(std::size_t i) {int arr[N]; /*fill*/ return arr[i];}。如果我忘记了模板参数,但这个推导是可以完成的,我肯定希望出现错误提示。 - chris
说起来,我不确定为什么它还需要作为函数参数传递。templ<func>(); 不就足够了吗? - chris
@chris:没错,我之前的问题写得不太清楚 :) 请看我的修改。 - HighCommander4
1
我认为这是不可能的。曾经有一个提案可以推断出template<using typename T, T t>中的T,但我认为它没有被采纳。 - chris
如果您愿意将函数作为参数传递,那么为什么还想将同一个函数作为模板参数呢?template <typename Function> templ(Function func); 有什么问题吗? - Igor Tandetnik
@IgorTandetnik:为了让编译器通过为每个作为参数传递的函数生成不同的实例(而不仅仅是每种类型的函数),从而更好地进行优化,然后在适当情况下将每个函数内联。 - HighCommander4
1个回答

3
函数的模板参数类型用于推导函数的模板实参。只有当该类型是允许的形式之一时,才能从类型中推导出模板实参。这些允许的形式在[temp.deduct.type]中指定。
模板实参可以在几个不同的上下文中被推导,但在每种情况下,都会将以模板参数为基础指定的类型(称其为P)与实际类型(称其为A)进行比较,并尝试找到模板实参值(类型、非类型参数的值或模板参数的模板),使得在替换推导出的值(称其为推导出的A)后,P与A兼容。
如果P和A具有以下形式之一,则可以推导模板类型实参T、模板模板实参TT或模板非类型实参i:
T cv-list T T* T& T[integer-constant] template-name(其中template-name引用类模板) type(*)(T) T(*)() T(*)(T) T type::* type T::* T T::* T(type::*)() type(T::*)() type(type::*)(T) type(T::*)(T) T(type::*)(T) T(T::*)() T(T::*)(T) type[i] template-name&lt;i>(其中template-name引用类模板) TT<T> TT<i> TT<>
其中(T)表示包含T的至少一个参数类型的参数列表,()表示不包含T的参数列表。类似地,表示至少一个参数包含T的模板实参列表,表示至少一个参数包含i的模板实参列表,而<>表示没有参数包含T或i的模板实参列表。
仅考虑非类型模板参数时,相关形式是包含i的那些形式:
type[i] template-name&lt;i>(其中template-name引用类模板) TT<i>
因此,不能直接从函数指针的值中推导出该值的值。但是,如果函数参数具有指定的形式之一,则可以推导非类型模板参数的值。
以下代码通过将非类型模板参数值包装在名为NonType的类模板中来实现此目的。 f的参数采用template-name<i>的形式,使其非类型模板参数的值可以被推导出来。
template<typename T, T value>
struct NonType {};

template<typename T, T value>
void f(NonType<T, value>)
{
}

void g();

struct A
{
    void f();
    int m;
};

int i;

#define MAKE_NONTYPE(value) NonType<decltype(value), (value)>()

int main()
{
    f(MAKE_NONTYPE(0)); // NonType<int, 0>
    f(MAKE_NONTYPE(&g)); // NonType<void(*)(), &g>
    f(MAKE_NONTYPE(&A::f)); // NonType<void(A::*)(), &A::f>
    f(MAKE_NONTYPE(&A::m)); // NonType<int A::*, &A::m>
    f(MAKE_NONTYPE(&i)); // NonType<int*, &i>
}

请注意,此处仅使用decltypeMAKE_NON_TYPE宏仅为方便起见,以避免编写完整的NonType模板参数列表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接