Date: Fri, 4 Mar 2022 18:08:53 +0100
T (&&arr)[N] is not a forwarding reference, so you shouldn’t use
std::forward here. Use std::move, because you intend to take this lvalue
and (unconditionally) turn it into an rvalue; that’s what std::move is for.
You’re totally right, @Arthur, thanks. I messed it up quite badly. So here
you are what a (hopefully correct) r-value specialization may look like:
<#cb1-1>template <std::size_t Idx, typename T, std::size_t N>
<#cb1-2>constexpr T&& get(T (&&arr)[N]) noexcept <#cb1-3>{ <#cb1-4>
static_assert(Idx < N, "Index out of bounds"); <#cb1-5> return
std::move(arr)[Idx]; <#cb1-6>}
@Lénárd’s observation about MSVC raises the question whether the return
type should be T&& or decltype(auto). In the latter case, calling get on an
r-value C-style array would return an l-value (on MSVC).
Again, I’d stick to what the standard already mandates for std::array. So,
in my opinion, the above specialization should return T&&.
Moreover, the decltype(auto) version would instanciate functions with
different signatures on different compilers, e.g.:
<#cb2-1>constexpr int& get<0, int, 3>(int (&&)[3]) noexcept; // MSVC
<#cb2-2>constexpr int&& get<0, int, 3>(int (&&)[3]) noexcept; //
Clang/GCC
I’m not really sure whether this is an issue, since the return type of
std::move(arr)[Idx] itself is not consistent across compilers.
As for the rest of the tuple-like interface for T[N], I’ve come up with
this:
<#cb3-1>namespace std <#cb3-2>{ <#cb3-3> <#cb3-4>template <typename
T, size_t N> <#cb3-5>struct tuple_size<T[N]> <#cb3-6> : public
integral_constant<size_t, N> { }; <#cb3-7> <#cb3-8>// I have to add
the following explicit specialization. If I don't, <#cb3-9>//
tuple_size<T const[N]> would match both the following <#cb3-10>//
specializations: <#cb3-11>// <#cb3-12>// 1. <U[N]> with U = T const
(i.e. the specialization above); <#cb3-13>// 2. <U const> with U =
T[N] (alredy in the standard). <#cb3-14>// <#cb3-15>// Resulting in a
compiler error. <#cb3-16>template <typename T, size_t N>
<#cb3-17>struct tuple_size<T const[N]> : public tuple_size<T[N]> { };
<#cb3-18> <#cb3-19>template <typename T, size_t N> <#cb3-20>struct
tuple_size<T volatile[N]> : public tuple_size<T[N]> { }; <#cb3-21>
<#cb3-22>template <typename T, size_t N> <#cb3-23>struct tuple_size<T
const volatile[N]> : public tuple_size<T[N]> { }; <#cb3-24>// --
<#cb3-25> <#cb3-26>namespace detail <#cb3-27>{ <#cb3-28> template
<size_t Idx, typename T> <#cb3-29> struct array_tuple_element;
<#cb3-30> <#cb3-31> template <size_t Idx, typename T, size_t N>
<#cb3-32> struct array_tuple_element<Idx, T[N]> <#cb3-33> {
<#cb3-34> static_assert(Idx < N, "Index out of bounds");
<#cb3-35> using type = T; <#cb3-36> }; <#cb3-37>} <#cb3-38>
<#cb3-39>template <size_t Idx, typename T, size_t N> <#cb3-40>struct
tuple_element<Idx, T[N]> <#cb3-41> : public
detail::array_tuple_element<Idx, T[N]> { }; <#cb3-42> <#cb3-43>// -- I
have to specify those for the same reason above. <#cb3-44>template
<size_t Idx, typename T, size_t N> <#cb3-45>struct tuple_element<Idx,
T const[N]> <#cb3-46> : public detail::array_tuple_element<Idx, T
const[N]> { }; <#cb3-47> <#cb3-48>template <size_t Idx, typename T,
size_t N> <#cb3-49>struct tuple_element<Idx, T volatile[N]> <#cb3-50>
: public detail::array_tuple_element<Idx, T volatile[N]> { };
<#cb3-51> <#cb3-52>template <size_t Idx, typename T, size_t N>
<#cb3-53>struct tuple_element<Idx, T const volatile[N]> <#cb3-54> :
public detail::array_tuple_element<Idx, T const volatile[N]> { };
<#cb3-55>// -- <#cb3-56>}
As suggested by @Zhihao, it’s true that a tuple-like interface is not
needed for structured bindings to C-style arrays. However, it would allow
for:
- std::apply (as suggested by @Lénárd);
- std::make_from_tuple;
- std::tuple_cat (implementations are allowed to support tuple-like
objects).
Here you are a link to play with:
https://godbolt.org/z/Ts3fn3YT4
(Note: I had to specialize struct std::__is_tuple_like_impl for T[N] in
order to pass "bye" to std::tuple_cat on Clang/GCC).
Il giorno mar 1 mar 2022 alle ore 19:36 Zhihao Yuan via Std-Proposals <
std-proposals_at_[hidden]> ha scritto:
> On Tuesday, March 1st, 2022 at 10:25 AM, Lénárd Szolnoki via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>
> > > [...] Structured bindings have dedicated
> > > rules for C arrays[dcl.struct.bind] (eel.is) and it kicks inearly,
> > > ignoring all tuple_* stuff.
> >
>
> > As you write, it wouldn't do anything for structured bindings, but it
> > would enable C arrays for `std::apply`.
> >
>
>
> Cool! Love to see it.
>
> --
> Zhihao Yuan, ID lichray
> The best way to predict the future is to invent it.
> _______________________________________________
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
std::forward here. Use std::move, because you intend to take this lvalue
and (unconditionally) turn it into an rvalue; that’s what std::move is for.
You’re totally right, @Arthur, thanks. I messed it up quite badly. So here
you are what a (hopefully correct) r-value specialization may look like:
<#cb1-1>template <std::size_t Idx, typename T, std::size_t N>
<#cb1-2>constexpr T&& get(T (&&arr)[N]) noexcept <#cb1-3>{ <#cb1-4>
static_assert(Idx < N, "Index out of bounds"); <#cb1-5> return
std::move(arr)[Idx]; <#cb1-6>}
@Lénárd’s observation about MSVC raises the question whether the return
type should be T&& or decltype(auto). In the latter case, calling get on an
r-value C-style array would return an l-value (on MSVC).
Again, I’d stick to what the standard already mandates for std::array. So,
in my opinion, the above specialization should return T&&.
Moreover, the decltype(auto) version would instanciate functions with
different signatures on different compilers, e.g.:
<#cb2-1>constexpr int& get<0, int, 3>(int (&&)[3]) noexcept; // MSVC
<#cb2-2>constexpr int&& get<0, int, 3>(int (&&)[3]) noexcept; //
Clang/GCC
I’m not really sure whether this is an issue, since the return type of
std::move(arr)[Idx] itself is not consistent across compilers.
As for the rest of the tuple-like interface for T[N], I’ve come up with
this:
<#cb3-1>namespace std <#cb3-2>{ <#cb3-3> <#cb3-4>template <typename
T, size_t N> <#cb3-5>struct tuple_size<T[N]> <#cb3-6> : public
integral_constant<size_t, N> { }; <#cb3-7> <#cb3-8>// I have to add
the following explicit specialization. If I don't, <#cb3-9>//
tuple_size<T const[N]> would match both the following <#cb3-10>//
specializations: <#cb3-11>// <#cb3-12>// 1. <U[N]> with U = T const
(i.e. the specialization above); <#cb3-13>// 2. <U const> with U =
T[N] (alredy in the standard). <#cb3-14>// <#cb3-15>// Resulting in a
compiler error. <#cb3-16>template <typename T, size_t N>
<#cb3-17>struct tuple_size<T const[N]> : public tuple_size<T[N]> { };
<#cb3-18> <#cb3-19>template <typename T, size_t N> <#cb3-20>struct
tuple_size<T volatile[N]> : public tuple_size<T[N]> { }; <#cb3-21>
<#cb3-22>template <typename T, size_t N> <#cb3-23>struct tuple_size<T
const volatile[N]> : public tuple_size<T[N]> { }; <#cb3-24>// --
<#cb3-25> <#cb3-26>namespace detail <#cb3-27>{ <#cb3-28> template
<size_t Idx, typename T> <#cb3-29> struct array_tuple_element;
<#cb3-30> <#cb3-31> template <size_t Idx, typename T, size_t N>
<#cb3-32> struct array_tuple_element<Idx, T[N]> <#cb3-33> {
<#cb3-34> static_assert(Idx < N, "Index out of bounds");
<#cb3-35> using type = T; <#cb3-36> }; <#cb3-37>} <#cb3-38>
<#cb3-39>template <size_t Idx, typename T, size_t N> <#cb3-40>struct
tuple_element<Idx, T[N]> <#cb3-41> : public
detail::array_tuple_element<Idx, T[N]> { }; <#cb3-42> <#cb3-43>// -- I
have to specify those for the same reason above. <#cb3-44>template
<size_t Idx, typename T, size_t N> <#cb3-45>struct tuple_element<Idx,
T const[N]> <#cb3-46> : public detail::array_tuple_element<Idx, T
const[N]> { }; <#cb3-47> <#cb3-48>template <size_t Idx, typename T,
size_t N> <#cb3-49>struct tuple_element<Idx, T volatile[N]> <#cb3-50>
: public detail::array_tuple_element<Idx, T volatile[N]> { };
<#cb3-51> <#cb3-52>template <size_t Idx, typename T, size_t N>
<#cb3-53>struct tuple_element<Idx, T const volatile[N]> <#cb3-54> :
public detail::array_tuple_element<Idx, T const volatile[N]> { };
<#cb3-55>// -- <#cb3-56>}
As suggested by @Zhihao, it’s true that a tuple-like interface is not
needed for structured bindings to C-style arrays. However, it would allow
for:
- std::apply (as suggested by @Lénárd);
- std::make_from_tuple;
- std::tuple_cat (implementations are allowed to support tuple-like
objects).
Here you are a link to play with:
https://godbolt.org/z/Ts3fn3YT4
(Note: I had to specialize struct std::__is_tuple_like_impl for T[N] in
order to pass "bye" to std::tuple_cat on Clang/GCC).
Il giorno mar 1 mar 2022 alle ore 19:36 Zhihao Yuan via Std-Proposals <
std-proposals_at_[hidden]> ha scritto:
> On Tuesday, March 1st, 2022 at 10:25 AM, Lénárd Szolnoki via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>
> > > [...] Structured bindings have dedicated
> > > rules for C arrays[dcl.struct.bind] (eel.is) and it kicks inearly,
> > > ignoring all tuple_* stuff.
> >
>
> > As you write, it wouldn't do anything for structured bindings, but it
> > would enable C arrays for `std::apply`.
> >
>
>
> Cool! Love to see it.
>
> --
> Zhihao Yuan, ID lichray
> The best way to predict the future is to invent it.
> _______________________________________________
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2022-03-04 17:06:12