Date: Sun, 3 Aug 2025 20:53:43 +0800
There was a bug in the code. forward_pointer_like should be like:
template <typename T, typename U>
constexpr auto forward_pointer_like(U* ptr)
{
constexpr bool is_adding_const =
is_const_v<remove_reference_t<T>>;
if constexpr (is_adding_const != is_const_v<U>) {
if constexpr (is_adding_const) {
return const_cast<const U*>(ptr);
} else {
return const_cast<remove_const_t<U>*>(ptr);
}
} else {
return ptr;
}
}
On Sun, 3 Aug 2025 at 14:09, Yongwei Wu <wuyongwei_at_[hidden]> wrote:
> While deducing this can be useful in some scenarios, it is prone to
> misuse. A simplified case is as follows:
>
> template <typename T>
> class Container {
> public:
> // …
> template <typename Self>
> auto data(this Self&& self) noexcept
> {
> return self.data_;
> }
>
> private:
> T* data_;
> };
>
> Generally you want the data() member function to give you a const T* on a
> const container, but this implementation will always give you T*.
> I nearly wrote such code in an article, and I *actually* saw similar code
> in a recently published C++ book.
>
> I think a utility function (as well as education materials) is useful in
> such cases. Modelling forward_like, probably something like:
>
> template <typename T, typename U>
> constexpr auto forward_pointer_like(U* ptr)
> {
> constexpr bool is_adding_const =
> is_const_v<remove_reference_t<T>>;
> if constexpr (is_const_v<remove_reference_t<T>> !=
> is_adding_const) {
> if constexpr (is_adding_const) {
> return const_cast<const U*>(ptr);
> } else {
> return const_cast<remove_const_t<U>*>(ptr);
> }
> } else {
> return ptr;
> }
> }
>
> Related idea: Should we just update as_const to make it generate a const
> T* given a T*? This could be an easy fix that allows us to simply use
> forward_like, but I am not sure whether it can break things.
>
> What do you think? Did I miss something already existing in the standard?
>
> --
> Yongwei Wu
> URL: http://wyw.dcweb.cn/
>
template <typename T, typename U>
constexpr auto forward_pointer_like(U* ptr)
{
constexpr bool is_adding_const =
is_const_v<remove_reference_t<T>>;
if constexpr (is_adding_const != is_const_v<U>) {
if constexpr (is_adding_const) {
return const_cast<const U*>(ptr);
} else {
return const_cast<remove_const_t<U>*>(ptr);
}
} else {
return ptr;
}
}
On Sun, 3 Aug 2025 at 14:09, Yongwei Wu <wuyongwei_at_[hidden]> wrote:
> While deducing this can be useful in some scenarios, it is prone to
> misuse. A simplified case is as follows:
>
> template <typename T>
> class Container {
> public:
> // …
> template <typename Self>
> auto data(this Self&& self) noexcept
> {
> return self.data_;
> }
>
> private:
> T* data_;
> };
>
> Generally you want the data() member function to give you a const T* on a
> const container, but this implementation will always give you T*.
> I nearly wrote such code in an article, and I *actually* saw similar code
> in a recently published C++ book.
>
> I think a utility function (as well as education materials) is useful in
> such cases. Modelling forward_like, probably something like:
>
> template <typename T, typename U>
> constexpr auto forward_pointer_like(U* ptr)
> {
> constexpr bool is_adding_const =
> is_const_v<remove_reference_t<T>>;
> if constexpr (is_const_v<remove_reference_t<T>> !=
> is_adding_const) {
> if constexpr (is_adding_const) {
> return const_cast<const U*>(ptr);
> } else {
> return const_cast<remove_const_t<U>*>(ptr);
> }
> } else {
> return ptr;
> }
> }
>
> Related idea: Should we just update as_const to make it generate a const
> T* given a T*? This could be an easy fix that allows us to simply use
> forward_like, but I am not sure whether it can break things.
>
> What do you think? Did I miss something already existing in the standard?
>
> --
> Yongwei Wu
> URL: http://wyw.dcweb.cn/
>
Received on 2025-08-03 12:53:58