8

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

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

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-qualification 或比 cv 更大的 cv-qualification,其中conversion - type-id表示类型“指向 (P1,...,Pn) 返回 R 的函数的指针”,或类型“对 (P1,...,Pn) 返回 R 的函数的指针的引用”,或类型“对 (P1,...,Pn) 返回 R 的函数的引用”,代理调用函数唯一名称调用函数并具有形式

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; }
};

我们也有别名模板。process给定第 14.5.7/2 节中的示例,其中冲突声明似乎在实例化之前发生了别名替换。在 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 );
}
4

1 回答 1

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 { ... }某个地方意外命中)。

于 2012-06-13T20:56:59.110 回答