来自提案第 4.2.7 节 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#pathological-cases
它说:
这些更不可能是真正有用的代码。在此示例中,
既不能转换为B
也不能转换为A
,因此这些函数甚至都无法使用普通成员语法调用。但是,您可以获取指向此类函数的指针并通过该指针调用它们。int
有效,如有奇怪请致电。(&B::bar)(42)
但是,它没有指定标准是否不允许将 type-of-self 的显式对象参数隐式转换为特定的另一种类型。
struct A { };
struct B {
void foo(this A&);
void bar(this int);
};
这是否意味着:
struct A { };
struct B {
operator A() const noexcept;
void foo(this A);
};
// ...
// '&B::foo' is of type function pointer not pointer to member function
// because it uses explicit object parameter.
(&B::foo)(A{});
(&B::foo)(B{});
B{}.foo(); // will work?
会起作用吗?
在另一种情况下,这是一个 lambda。由于 lambda 的类型是不可言说的并且始终是相关的。那上面的案例呢? (这个无捕获 lambda 可转换为
int(*)(int, int, int)
)
auto fib = [](this int(* self)(int, int, int), int n, int a = 0, int b = 1) {
return n == 0 ? a : n == 1 ? b : self(n - 1, b, a + b);
};
鉴于:
非成员函数、静态成员函数和显式对象成员函数匹配函数指针类型或对函数类型的引用的目标。非静态隐式对象成员函数与指向成员函数类型的指针匹配。 ([over.match.viable] §12.2.3)
在所有上下文中,当转换为隐式对象参数或转换为赋值运算的左操作数时,仅允许使用标准转换序列。 [注意:转换为显式对象参数时,如果有的话,允许用户定义的转换序列。 - 尾注]([over.best.ics] §12.2.4.2)
对于你的第一个问题:
struct A { };
struct B {
operator A() const noexcept;
void foo(this A);
};
B{}.foo(); // will work?
是的。候选查找将找到
B::foo
,它或多或少地计算为 foo(B{})
,由于转换函数,它是有效的。这在您引用的注释中明确指出,在 [over.ics.best]/7:
[注5:转换为显式对象参数时,如果有的话,允许用户定义的转换序列。 — 尾注]
这个,我其实不太确定:
auto fib = [](this int(* self)(int, int, int), int n, int a = 0, int b = 1) {
return n == 0 ? a : n == 1 ? b : self(n - 1, b, a + b);
};
它似乎不太可能有用,你可能永远不应该这样做,所以我不知道它是否真的有效是否重要。但也不确定这样的例子意味着什么:
struct C {
C(auto);
void f();
};
auto lambda = [](this C c) { c.f(); }; // OK?
如果 this 可转换为函数指针,那么函数指针到底是什么类型?如果是
void(*)()
,那么我们要在哪个 C
上调用 f()
?所以它必须是 void(*)(C)
,在这种情况下 fib
示例肯定不起作用,因为不可能以匹配的方式非通用地拼写函数指针类型。