Home > Enterprise >  Does Explicit Object Parameter Allow Convertible Types?
Does Explicit Object Parameter Allow Convertible Types?

Time:02-14

From §4.2.7 of the proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#pathological-cases

It said that:

These are even more unlikely to be actually useful code. In this example, B is neither convertible to A nor int, so neither of these functions is even invocable using normal member syntax. However, you could take a pointer to such functions and invoke them through that pointer. (&B::bar)(42) is valid, if weird, call.

However, It does not specify whether the standard does not allow explicit object parameter of type-of-self implicitly convertible to particular another type.

struct A { };
struct B {
    void foo(this A&);
    void bar(this int);
};

Does that mean that:

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?

will work?

In another case, here is a lambda. Since the type of the lambda is unutterable and is always dependent. What about the case above? (this captureless lambda is convertible to 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);
};

Given that:

Non-member functions, static member functions, and explicit object member functions match targets of function pointer type or reference to function type. Non-static Implicit object member functions match targets of pointer-to-member-function type. ([over.match.viable] §12.2.3)

In all contexts, when converting to the implicit object parameter or when converting to the left operand of an assignment operation only standard conversion sequences are allowed. [Note: When converting to the explicit object parameter, if any, user-defined conversion sequences are allowed. - end note ] ([over.best.ics] §12.2.4.2)

CodePudding user response:

For your first question:

struct A { };
struct B {
  operator A() const noexcept;
  void foo(this A);
};

B{}.foo(); // will work?

Yes. Candidate lookup will find B::foo, which more or less evaluates as foo(B{}), which is valid due to the conversion function. This is explicitly called out in the note you cited, in [over.ics.best]/7:

[Note 5: When converting to the explicit object parameter, if any, user-defined conversion sequences are allowed. — end note]


This one is likewise valid, for the same reason:

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

Although, this one seems exceedingly unlikely to be useful and you should probably never do this.

  • Related