C++ Logo


Advanced search

Re: Ambiguous specification of INVOKE?

From: Eric Schmidt <eric41293_at_[hidden]>
Date: Sun, 30 Oct 2022 12:01:11 -0700
Some possible resolutions are:

1. Prefer the first (non reference_wrapper) case.
2. Prefer the second case.
3. The choice is unspecified.
4. is_invocable is false. std::invoke is ill-formed.

I think option 1 is best. This is because if the condition for the first
alternative holds, the second alternative will almost certainly result
in an ill-formed expression.

I mean, it's possible to create a situation where the second is
well-formed, but I had to go through some contortions to come up with an

struct S{};

class std::reference_wrapper<S>
     Insert implementation according to the standard


     int x;

using refwrap = std::reference_wrapper<S>;

class std::reference_wrapper<refwrap> : private refwrap
     static S s;

     Insert implementation according to the standard
     Constructors initialize refwrap base with s

template<class T>
constexpr bool is_reference_wrapper = false;

template<class T>
constexpr bool is_reference_wrapper<std::reference_wrapper<T>> = true;

void foo(std::reference_wrapper<refwrap> t1)
     using T = refwrap;
     auto f = &T::x;

     // First case condition holds
         std::is_base_of_v<T, std::remove_reference_t<decltype(t1)>>);

     // Ill-formed because refwrap is a private base
     // Changing it to public makes this well-formed

     // Second case condition holds

     // Well-formed

On 10/30/22 2:10 AM, Lénárd Szolnoki via Std-Discussion wrote:
> Hi,
> That indeed looks ambiguous to me. I expected the typical "if A is well
> formed then do A else if ..." chain in the standard wording, but it's
> not used here.
> Cheers,
> Lénárd
> On 30 October 2022 03:29:41 CET, Eric Schmidt via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> In [func.require], we have the definition of INVOKE(f, t1, t2, ..., tN), which has a number of cases. The first two are
> (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and is_­base_­of_­v<T, remove_­reference_­t<decltype(t1)>> is true;
> and
> (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of a class T and remove_­cvref_­t<decltype(t1)> is a specialization of reference_­wrapper;
> There is no mention of which of these is to be preferred if the conditions hold in both cases.
> So, given
> using refwrap = std::reference_wrapper<int>;
> is std::is_invocable_v<void (refwrap::*)(), refwrap> true? If the first case takes precedence, then yes. If the second case, then no.
> A quick check on Godbolt shows that GCC and Clang evaluate the previous expression to false, while MSVC evaluates it to true.
> A similar ambiguity exists for the pointer-to-data-member case.
> (I had originally written the following example, which yields a compiler error on GCC and Clang, but is accepted by MSVC. But then I realized that it violates the general ban on creating pointers to library functions.)
> void f(std::reference_wrapper<int> x)
> {
> std::invoke(&std::reference_wrapper<int>::get, x);
> }
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion>

Received on 2022-10-30 19:01:44