将函数模板化转换为函数指针

Templated conversion function to function pointer

本文关键字:函数 指针 转换 函数模板      更新时间:2023-10-16

耶,另一个由随机序列C++术语组成的问题标题!

通常我们通过实现 operator() 来使类 Callable。但您也可以通过实现到函数指针或引用类型的用户定义转换来执行此操作。转换函数可以返回指向函数的指针,而不是使用完美转发,然后使用原始参数列表调用该函数。

struct call_printf {
    typedef int printf_t( char const *, ... );
    operator printf_t & () { return std::printf; }
};

http://ideone.com/kqrJz

据我所知,上面的typedef是句法上的必需品。转换函数的名称由类型说明符 seq 组成,它不允许像 int (*)() 这样的构造。这将需要一个抽象声明器。据推测,原因是此类类型名称变得复杂,并且用作对象名称的复杂构造很难解析。

转换函数也允许模板化,但必须推导模板参数,因为没有地方可以显式指定它们。(这将破坏隐式转换的全部要点。


问题 #1:在 C++03 中,是否无法指定函数转换运算符模板?似乎没有办法在可接受的函数指针类型中解析模板参数(即在推导的上下文中命名它们)。

这是来自 C++11, §13.3.1.1.2/2 [over.call.object] 的等效引用。从 C++03 开始基本相同:

此外,对于在 T 中声明的每个非显式转换函数的形式

operator conversion-type-id () cv-qualifier attribute-specifier-seqopt;

其中 cv 限定符与 cv 相同,或比 cv 更符合 cv并且 转换类型-id 表示类型"指向返回 R 的 (P1,...,Pn) 函数的指针",或类型"引用返回 R 的 (P1,...,Pn) 函数的指针",或类型"引用返回 R 的 (P1,...,Pn) 函数",具有唯一名称调用函数并具有

R call-function ( conversion-type-id F, P1 a1, ... ,Pn an) { return F (a1,... ,an); }

也被视为候选函数。同样,代理项调用函数被添加到在 T 基类中声明的每个非显式转换函数的候选函数集中,前提是 函数不会通过另一个干预声明隐藏在 T 中。


问题 #2:在 C++11 中,是否可以使用默认模板参数指定此类转换?这对SFINAE很有用。这里与上面示例的唯一区别是转换类型ID 仅表示实例化后的函数引用,因为它是依赖类型(尽管不变性)。这会触发 GCC 并跳过成员模板。

enum { call_alternate = true; }
struct call_switch {
    template< bool en = call_alternate >
    operator typename std::enable_if< en, decltype( fn_1 ) & >::type ()
        { return fn_1; }
    template< bool en = ! call_alternate >
    operator typename std::enable_if< en, decltype( fn_2 ) & >::type ()
        { return fn_2; }
};

我们也有别名模板。似乎别名替换发生在实例化之前,给定 §14.5.7/2 中的示例,其中 的声明process冲突。在 GCC 4.7 中,此代码至少实例化了声明,但随后它会产生一个奇怪的"候选者期望 2 个参数,提供 2 个"错误。

template< typename t >
using fn_t = void (&)( t );
struct talk {
    template< typename t >
    operator fn_t< t >() { return fn; }
};
int main() {
    talk()( 3 );
}

问题 #1:在 C++03 中,是否无法指定函数转换运算符模板?似乎没有办法在可接受的函数指针类型中解析模板参数(即在推导的上下文中命名它们)。

是的,这是正确的。

问题 #2:在 C++11 中,是否可以使用默认模板参数指定此类转换?

它可以,您也可以使用别名模板,但不能使用此类转换函数模板来创建代理项调用函数。否则,您可以使用它将类对象转换为隐式转换中的函数指针。

我们也有别名模板。似乎别名替换发生在实例化之前,给定 §14.5.7/2 中的示例,其中进程声明冲突。在 GCC 4.7 中,此代码至少实例化了声明,但随后它会产生一个奇怪的"候选者期望 2 个参数,提供 2 个"错误。

是的,这是 https://groups.google.com/forum/?fromgroups#!topic/comp.std.c++/lXLFBcF_m3c 的(并导致 DR395 关闭),但即使这样的转换函数模板可以在 void(&p)() = yourClassObject 等情况下工作,它不适用于代理项调用函数,因为那里的转换函数需要提供一个固定的非依赖类型,当调用代理项函数时,类对象将转换为该类型, 但是转换函数模板通常不提供这样的类型(除了template<typename = int> operator Identity<void(*)()>();之类的奇怪事情......

我认为 GCC 可能会错误地生成具有依赖类型的候选call-function(void (&)( t ), t)并尝试调用该候选类型,从而违反它的某些不变性(这可以解释奇怪的错误消息 - 可能会意外地击中} else { ... }某处)。