来自提案的§4.2.7 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#pathological-cases
它说:
这些更不可能是真正有用的代码。在这个例子中,
B
既不能转换为A
也不能转换为int
,所以这两个函数都不能使用普通成员语法调用。但是,您可以获取指向此类函数的指针,并通过该指针调用它们。(&B::bar)(42)
是有效的,如果奇怪,请致电。
但是,它没有指定标准是否不允许将自身类型的显式对象参数隐式转换为特定的另一种类型。
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 的类型是不可说的,并且始终是依赖的。上面的案例呢?(这种无法捕获的λ可转换为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?
如果这可以转换为函数指针,那么函数指针类型到底是什么?如果是void(*)()
,那么我们在哪个C
上调用f()
?所以它必须void(*)(C)
,在这种情况下,fib
示例肯定不起作用,因为不可能以匹配的方式非通用地拼写函数指针类型。