C++ Logo

std-discussion

Advanced search

Re: Ambiguous specification of INVOKE?

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Sun, 30 Oct 2022 21:51:26 +0100
Hi,

On 30 October 2022 20:01:11 CET, Eric Schmidt via Std-Discussion <std-discussion_at_[hidden]> wrote:
>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 agree that option 1 is the best. Anything that narrows down the special casing of reference_wrapper is a win in my book.

>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 example:
>
>struct S{};
>
>template<>
>class std::reference_wrapper<S>
>{
> Insert implementation according to the standard
>
>public:
>
> int x;
>};
>
>using refwrap = std::reference_wrapper<S>;
>
>template<>
>class std::reference_wrapper<refwrap> : private refwrap
>{
> static S s;
>
> Insert implementation according to the standard
> Constructors initialize refwrap base with s
>};

I don't think that you are allowed to specialise like this. Your specialisation must satisfy the requirements of the non-specialised type, but there is no allowance to derive from an other, unspecified standard library type, even privately.

>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
> static_assert(
> 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
> t1.*f;
>
> // Second case condition holds
> static_assert(is_reference_wrapper<T>);
>
> // Well-formed
> t1.get().*f;
>}
>
>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 20:51:38