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>

>>

>>

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