模板化转换函数到函数指针

时间:2012-06-13 15:57:21

标签: c++ templates c++11 type-conversion functor

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

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

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

http://ideone.com/kqrJz

据我所知,上面的typedef是句法必需品。转换函数的名称由 type-specifier-seq 组成,它不允许像int (*)()这样的构造。这需要一个 abstract-declarator 。可能原因是这样的类型名称变得复杂,并且用作对象名称的复杂构造很难解析。

转换函数也允许模板化,但必须推导出模板参数,因为无处可明确指定它们。 (那会破坏隐含转换的全部意义。)


问题#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-qualifier 与cv资格相同,或者是比 cv 转换类型更高的cv资格-id 表示类型“指向(P1,...,Pn)函数的指针返回R”,或类型“指向函数指针(P1,...,Pn)返回R“,或类型”引用函数(P1,...,Pn)返回R“,一个代理调用函数,具有唯一名称call-function并具有形式

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

也被视为候选函数。类似地,代理调用函数被添加到候选函数集中,用于在T的基类中声明的每个非显式转换函数。   函数不会被另一个干预声明隐藏在T中。


问题#2:在C ++ 11中,是否可以使用默认模板参数指定此类转换?这对SFINAE很有用。与上面示例的唯一区别在于 conversion-type-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 个答案:

答案 0 :(得分: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),并试图调用该候选,从而违反了它的某些不变量(这可以解释奇怪的错误消息 - 可能会命中{{意外地在某处)。