Pot*_*ter 8 c++ templates type-conversion functor c++11
是的,另一个问题标题由随机的C++术语序列组成!
通常我们通过实现来创建一个Callable类operator()
.但您也可以通过实现用户定义的函数指针或引用类型转换来实现.转换函数可以返回指向函数的指针,而不是使用完美转发,然后使用原始参数列表调用该函数.
struct call_printf {
typedef int printf_t( char const *, ... );
operator printf_t & () { return std::printf; }
};
Run Code Online (Sandbox Code Playgroud)
据我所知,typedef
上述内容是一种语法必需品.转换函数的名称由type-specifier-seq构成,它不允许类似的构造int (*)()
.那需要一个抽象声明器.可能原因是这样的类型名称变得复杂,并且用作对象名称的复杂构造很难解析.
转换函数也允许模板化,但必须推导出模板参数,因为无处可明确指定它们.(那会破坏隐含转换的全部意义.)
问题1:在C++ 03中,是否无法指定函数转换运算符模板?似乎无法在可接受的函数指针类型中解析模板参数(即,在推导的上下文中命名它们).
这是来自C++ 11,§13.3.1.1.2/ 2 [over.call.object]的等效参考.它与C++ 03基本相同:
另外,对于在表单的T中声明的每个非显式转换函数
Run Code Online (Sandbox Code Playgroud)operator conversion-type-id () cv-qualifier attribute-specifier-seqopt;
其中cv-qualifier与cv-qualification相同,或cv资格比cv更高,并且其中conversion-type-id表示类型"指向(P1,...,Pn)返回R的函数的指针",或类型"引用指向(P1,...,Pn)函数的指针返回R",或类型"引用函数(P1,...,Pn)返回R",代理调用函数与唯一名称调用函数并具有表单
Run Code Online (Sandbox Code Playgroud)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; }
};
Run Code Online (Sandbox Code Playgroud)
我们还有别名模板.似乎在实例化之前发生了别名替换,给出了§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 );
}
Run Code Online (Sandbox Code Playgroud)
\n\n\n问题#1:在C++03中,是否无法指定函数转换运算符模板?似乎无法以可接受的函数指针类型解析模板参数(即,在推导的上下文中命名它们)。
\n
对,那是正确的。
\n\n\n\n\n问题 #2:在 C++11 中,可以使用默认模板参数指定这样的转换吗?
\n
可以,您也可以使用别名模板,但不能使用此类转换函数模板来创建代理调用函数。否则,您可以使用它在隐式转换中将类对象转换为函数指针。
\n\n\n\n\n我们还有别名模板。考虑到 \xc2\xa714.5.7/2 中的示例,其中进程声明发生冲突,别名替换似乎发生在实例化之前。在 GCC 4.7 中,这段代码至少实例化了声明,但随后它产生了一个奇怪的“候选者需要 2 个参数,提供了 2 个”错误。
\n
是的,这是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 { ... }
)。