C++ Logo

std-proposals

Advanced search

Re: [std-proposals] C-style array specialization for std::get

From: Paolo Di Giglio <p.digiglio91_at_[hidden]>
Date: Thu, 10 Mar 2022 13:22:33 +0100
Thanks @Arthur and @Zhihao.

About these two comments:

> You can (and should) std::move(arr[Idx]).

and

> Because of everything you said about MSVC, I suggest that your proposal
> (and vendors' implementations too) should actually say
>
> return std::move(arr[Idx]); // correct in practice
>
> not
>
> return std::move(arr)[Idx]; // pedantically correct but fails on MSVC
>
> The effect is the same, on everything-but-MSVC; and on MSVC the former
> is preferable. (AFAIK, both are equally correct as far as the paper
> standard is concerned.)

I can't understand whether there's a real difference in either of the
return statements if the return type of std::get is T&&. I'd expect an
implicit cast to T&& on MSCV (and no cast on other compilers). Am I
missing something here?

> In your actual proposal, please don't overspecify that tuple_size<X>
> must inherit from tuple_size<Y>; that puts unnecessary and inefficient
> constraints on the vendor.

Why would you consider this an overspecification? My point here is the
tuple size of any cv-qualified array is the same as the unqualified
array.

>From https://en.cppreference.com/w/cpp/utility/tuple_size I can see the
standard implementation does it differently:

    template< class T >
    struct tuple_size< const T >
        : integral_constant<size_t, tuple_size<T>::value> { };

But, still, tuple_size<T const> instanciates tuple_size<T>.

Also, from the page linked above, I learn the volatile specializations
are deprecated (both for tuple_size and tuple_element). So I should drop
them from the proposal.

> However, I think you could actually get away with only this C++20ism
> instead:
>
> template<class T, size_t N>
> requires (!is_same_v<T, remove_cv_t<T>>)
> struct tuple_size<T[N]> : public integral_constant<size_t, N> { };

Thanks for this suggestion: I would have never considered it. I don't
understand the negation in the requires clause: did you actually mean
the following?

    requires (is_same_v<T, remove_cv_t<T>>)

> I don't understand why you need this layer of indirection at all, even
> before applying the trick above.

I needed to implement specializations of tuple_element for cv-qualified
arrays; otherwise tuple_element<T const[N]>, say, would match both the
newly introduced <T[N]> and the existing <T const> specializations. I
didn't want to reimplement tuple_element four times, so I delegated to
array_tuple_element. Is this bad somehow?

> Also, and slightly scarier, but still not too bad:
>
> auto a = std::make_tuple("hello");
> assert(std::tuple_size_v<decltype(a)> == 1); // OK
>
> auto b = std::make_from_tuple("world");
> assert(std::tuple_size_v<decltype(b)> == 6); // cursed
>

I don't think this code is going to be an issue as it doesn't compile:
you must pass the type you want to construct to std::make_from_tuple as
an explicit template parameter. However, if I get your point:

 * "hello" is treated as a char const* (because std::make_tuple applies
 std::decay to its arguments);

 * "world" is treated as a char const(&)[6];

And this may lead to confusion or inconsistency. Is this correct?

Received on 2022-03-10 12:19:48