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
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
};
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>
>
>
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
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
};
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 19:01:44