这是对这个问题的后续:为什么我可以调用一个 const 引用的可调用函数,而实际的可调用函数是一个可变的 lambda?
当像这样调用时,我可以模仿 const 可变闭包到函数指针的隐式转换:
#include <iostream>
void dummy() { std::cout << "call dummy\n";}
using fptr = void(*)();
struct callable {
void operator()() {std::cout << "call callable\n"; }
operator fptr() const {
std::cout << "convert\n";
return &dummy;
}
};
int main()
{
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn();
};
exec( callable{} );
}
我不明白为什么会触发隐式转换。它在传递参数时不会发生(参数是推导出来的,因此不考虑隐式转换),而仅在()
调用时才会发生,正如输出所示:
call
convert
call dummy
我尝试进行实验来了解发生了什么。尝试使用其他运算符进行类似操作,但没有成功(正如预期的那样):
#include <iostream>
struct bar {
void operator+(int) const { std::cout << "bar\n"; };
};
struct foo {
void operator+(int) { std::cout << "foo\n"; }
operator bar() const { return {}; }
};
int main() {
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn + 42;
};
exec(foo{});
};
错误:
<source>:15:12: error: passing 'const foo' as 'this' argument discards qualifiers [-fpermissive]
15 | fn + 42;
| ~~~^~~~
调用运算符有什么特殊之处?它怎么可能fn()
隐式转换fn
为调用其他函数呢?
PS:同时我发现它一定与转换为函数指针有关,因为当我将第一个例子转换为比第二个更相似的例子时,我得到了预期的错误:
#include <iostream>
struct foo {
void operator()() const { std::cout << "call foo\n"; }
};
struct callable {
void operator()() {std::cout << "call callable\n"; }
operator foo() const { return {}; };
};
int main()
{
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn();
};
exec( callable{} );
}
现在我得到了预期的错误
<source>:17:11: error: no match for call to '(const callable) ()'
17 | fn();
| ~~^~
现在的问题是:转换为函数指针类型有什么特殊之处?
看起来它fn()
考虑了函数指针的转换。为什么?
转换为函数类型的这种特殊行为在[over.call.object]中定义:
在您的示例中,后缀表达式
E
为fn
,其计算结果为 类型的类对象const callable
。由于callable
具有转换为“指向()
返回函数的指针void
”的转换运算符,并且该运算符是const
-qualified,因此代理函数void unique_name(fptr F) { return F(); }
被想象,并且调用unique_name((fn))
被视为对表达式 的调用fn()
。为了真正使
+
情况等效,它应该是这样的:然后它确实编译并输出:
不同之处在于成员
bar::operator+(int)
不是候选者,而非成员::operator+(bar const&, int)
是(因为它在范围内;如果将其包装在命名空间中,则无法通过 ADL 找到它 - 转换运算符返回类型不会以这种方式对 ADL 做出贡献)。整数确实定义了这样的内置非成员运算符,如[over.built/10]中所述。调用表达式的行为在[over.call.object]中单独定义,但本质上与非成员
::operator()(F*)
是一个事物并为每个函数类型定义的行为相同F
。