2

来自提案的§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 的类型是不可言说的并且总是依赖的。上面的案例呢?(这个无捕获的 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)

4

1 回答 1

4

对于你的第一个问题:

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例子肯定不起作用,因为不可能以匹配的方式非通用地拼写函数指针类型。

于 2022-02-14T06:33:32.527 回答