Date: Sat, 12 Oct 2024 14:59:59 +0100
On Sat, 12 Oct 2024 at 14:37, Robin Savonen Söderholm via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> I generally avoid inheritance, but it is (sadly) the only ABI-safe and
> portable solution (AFAIK) to optimise away empty members.
>
But doesn't work for final classes, or ones with protected constructors.
But why would the protected constructor be an issue?
If you're using inheritance instead of using a data member, because you
want the empty base optimization, then a protected constructor would not
have been usable anyway. So why do you care about this case? If the type is
awkward and unusable and breaks code that tries to use it, that's the
type's problem. The user who tries to instantiate your class template with
an unusable type can deal with it.
> [[no_unique_address]] is not supported by Clang for windows, MSVC only
> allows the MSVC-prefixed version.
>
What an absolute s***show that has turned out to be :-(
> But maybe I should tackle that instead...
>
>
> // Robin
>
> On Sat, Oct 12, 2024 at 3:27 PM Jonathan Wakely <cxx_at_[hidden]> wrote:
>
>>
>>
>> On Sat, 12 Oct 2024 at 14:22, Jonathan Wakely <cxx_at_[hidden]> wrote:
>>
>>>
>>>
>>> On Sat, 12 Oct 2024 at 14:05, Robin Savonen Söderholm via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>>
>>>> Hi!
>>>> I can't seem to find any information about how constraints and class
>>>> access specifiers could work together. Currently it seems like concepts are
>>>> always evaluated from "outside" a class (i.e. only public fields and
>>>> methods affect the evaluation of the constraint).
>>>>
>>>
>>> Correct. That's essential to be able to cache the result, you don't want
>>> foo<T> to mean different things depending on where the first check happens,
>>> and you don't want the result to not be memoized (for performance during
>>> compilation).
>>>
>>>
>>>
>>>> E.g. for example:
>>>> ```c++
>>>>
>>>> #include <concepts>
>>>> #include <utility>
>>>>
>>>> class clazz_with_protected_ctor {
>>>> protected:
>>>> explicit clazz_with_protected_ctor(int) {}
>>>> };
>>>>
>>>> template <typename T>
>>>> struct clazz_wrapper: private T {
>>>> template <typename... Ts>
>>>> requires(std::constructible_from<T, Ts&&...>) explicit
>>>> clazz_wrapper(Ts&&... args) : T(std::forward<T>(args)...) {}
>>>> };
>>>>
>>>> void foo() {
>>>> // Generates compile-error since the constraint for the constructor
>>>> can't see the protected constructor
>>>> auto my_wrapper = clazz_wrapper<clazz_with_protected_ctor>(1);
>>>> }
>>>> ```
>>>>
>>>> I wonder if not the above code should actually be a sensible piece of
>>>> code (especially with CRTP-based API:s), and it may be needed that
>>>> constraints are evaluated from the context that they are used.
>>>>
>>>
>>> Inheritance is an incredibly strong coupling, the strongest in C++. It
>>> doesn't really make sense to be inheriting from types where you don't know
>>> whether you can use a protected (or private, via friendship) API. Either
>>> inherit from arbitrary types provided as template args and rely on the
>>> members you need being public
>>>
>>
>> I should have said "being accessible" rather than necessarily public.
>> It's fine if the member you need to use is protected, because the derived
>> class will be able to use it. It doesn't make sense to query whether it's
>> usable though. If you're inheriting,the member you need to use need to be
>> accessible
>>
>> N.B. In the example above, you could just do `using T::T;` to get the
>> desired effects. So you don't need to test for the constructor being usable.
>>
>>
>>
>>> , or don't inherit.
>>>
>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
std-proposals_at_[hidden]> wrote:
> I generally avoid inheritance, but it is (sadly) the only ABI-safe and
> portable solution (AFAIK) to optimise away empty members.
>
But doesn't work for final classes, or ones with protected constructors.
But why would the protected constructor be an issue?
If you're using inheritance instead of using a data member, because you
want the empty base optimization, then a protected constructor would not
have been usable anyway. So why do you care about this case? If the type is
awkward and unusable and breaks code that tries to use it, that's the
type's problem. The user who tries to instantiate your class template with
an unusable type can deal with it.
> [[no_unique_address]] is not supported by Clang for windows, MSVC only
> allows the MSVC-prefixed version.
>
What an absolute s***show that has turned out to be :-(
> But maybe I should tackle that instead...
>
>
> // Robin
>
> On Sat, Oct 12, 2024 at 3:27 PM Jonathan Wakely <cxx_at_[hidden]> wrote:
>
>>
>>
>> On Sat, 12 Oct 2024 at 14:22, Jonathan Wakely <cxx_at_[hidden]> wrote:
>>
>>>
>>>
>>> On Sat, 12 Oct 2024 at 14:05, Robin Savonen Söderholm via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>>
>>>> Hi!
>>>> I can't seem to find any information about how constraints and class
>>>> access specifiers could work together. Currently it seems like concepts are
>>>> always evaluated from "outside" a class (i.e. only public fields and
>>>> methods affect the evaluation of the constraint).
>>>>
>>>
>>> Correct. That's essential to be able to cache the result, you don't want
>>> foo<T> to mean different things depending on where the first check happens,
>>> and you don't want the result to not be memoized (for performance during
>>> compilation).
>>>
>>>
>>>
>>>> E.g. for example:
>>>> ```c++
>>>>
>>>> #include <concepts>
>>>> #include <utility>
>>>>
>>>> class clazz_with_protected_ctor {
>>>> protected:
>>>> explicit clazz_with_protected_ctor(int) {}
>>>> };
>>>>
>>>> template <typename T>
>>>> struct clazz_wrapper: private T {
>>>> template <typename... Ts>
>>>> requires(std::constructible_from<T, Ts&&...>) explicit
>>>> clazz_wrapper(Ts&&... args) : T(std::forward<T>(args)...) {}
>>>> };
>>>>
>>>> void foo() {
>>>> // Generates compile-error since the constraint for the constructor
>>>> can't see the protected constructor
>>>> auto my_wrapper = clazz_wrapper<clazz_with_protected_ctor>(1);
>>>> }
>>>> ```
>>>>
>>>> I wonder if not the above code should actually be a sensible piece of
>>>> code (especially with CRTP-based API:s), and it may be needed that
>>>> constraints are evaluated from the context that they are used.
>>>>
>>>
>>> Inheritance is an incredibly strong coupling, the strongest in C++. It
>>> doesn't really make sense to be inheriting from types where you don't know
>>> whether you can use a protected (or private, via friendship) API. Either
>>> inherit from arbitrary types provided as template args and rely on the
>>> members you need being public
>>>
>>
>> I should have said "being accessible" rather than necessarily public.
>> It's fine if the member you need to use is protected, because the derived
>> class will be able to use it. It doesn't make sense to query whether it's
>> usable though. If you're inheriting,the member you need to use need to be
>> accessible
>>
>> N.B. In the example above, you could just do `using T::T;` to get the
>> desired effects. So you don't need to test for the constructor being usable.
>>
>>
>>
>>> , or don't inherit.
>>>
>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2024-10-12 14:01:20