C++ Logo

std-proposals

Advanced search

Re: Allow use of constants and types from not-instantiation-ed template

From: Maciej Polanski <maciejpolanski75_at_[hidden]>
Date: Wed, 22 Dec 2021 02:10:42 +0100
On 20.12.2021 22:44, Brian Bi wrote:
> On Sun, Dec 19, 2021 at 2:36 PM Maciej Polanski via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> Hello,
>>
>> So, let's just allow use of a types and static constants from
>> not-instantiation-ed template class (if possible):
>>
>> template<typename T>
>> struct Foo{
>> constexpr static int bar = 0x01;
>> };
>> int i = Foo::bar; // Doesn't compile now, but could
>> int j = Foo<class Nonexisting>::bar; // Ugly hack but works
>>
>>
>> This improvement should make template configuration behave similar to
>> class usages.
>> Classes frequently uses internal class constants, e.g.
>> "std::cout.setf(std::ios::hex, std::ios::basefield)".
>>
>> So this change would allow to include constants and types used to
>> configure template, inside it.
>> Ideally, those should be visible inside template-initialization-list
>> without scope operators.
>>
>> HelloWorld example:
>> ---
>> #include <iostream>
>>
>> // T needed for some functionality not listed here
>> template <typename T, int flags>
>> struct HelloWorld
>> {
>> static constexpr int Hello = 0x1;
>> static constexpr int World = 0x2;
>> static constexpr int Exclamation = 0x4;
>>
>> void work(){
>> if (flags & Hello) // <- no code for this line
>> std::cout << "Hello ";
>> if (flags & World) // <- no code for this line
>> std::cout << "World";
>> if (flags & Exclamation) // <- no code for this line
>> std::cout << "!\n";
>> }
>> };
>>
>> int main()
>> {
>> HelloWorld<int, Hello | World | Exclamation>{}.work(); // Prints
>> "Hello World!"
>> return 0;
>> }
>> ---
>>
>> Currently types and constants have to be defined before template,
>> polluting higher scope. Or ugly hack used:
>>
>> HelloWorld<int, HelloWorld<void*, 0>::Hello |
>> HelloWorld<std::iostream, -1>::World | HelloWorld<class Nonexisting,
>> 3141592>::Exclamation>{}.work();
>>
>> You can see this yourself in Compiler Explorer:
>> https://godbolt.org/z/s6MP7sqz5
>>
>>
>> Regards,
>> Maciej
>>
>>
>>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
> The standard workaround for the OP's use case is to move the constants into
> a non-template public base class like std::ios_base. Any proposal for a new
> language feature will need to explain why the current workarounds are
> inadequate (e.g., too much code duplication). I personally can't see much
> wrong with the status quo.
>

Yes, using public non template base class seems to be a good solution to
a problem with partial specialization that may redefine constants.

But, regarding the main objection, lets have:

struct bar {
     constexpr static int flag1 = 0x1;
};

template <typename T, int flags>
struct foo : public bar {
     T t;
     void work() {}
};

So, how can I make a variable from `foo` template?

   foo<int, bar::flag1> f1;
This approach has a major flow: no obvious link between `foo` and `bar`.
Me and you know the hierarchy. And the example you gave with ios is also
valid, everybody knows (or should) that hierarchy.
But in general case, users does not need to know if template has a base
and what is it.
And, more importantly, users can mix up base classes names and use the
wrong one.

   foo<int, foo::flag1> f2;
This is class-like, as when declaring variable of derived class, the
flags from base are visible through derived class name.
But it doesn't compile. Even though the value of flag1 is unambiguous,
foo's template arguments must be provided.

   foo<int, foo<int, 3141592>::flag1> f3;
This works, but 3141592 is plainly stupid and 0 wouldn't be less magic
either.
However, attempt to use "flag1", that could make more sense, generates
recurrence in declaration.

So, I just think that use like `foo<int, foo::flag1> f2` or even better
`foo<int, flag1> f2` should be possible.

Thanks,
Maciej

Received on 2021-12-21 19:10:48