I completely agree that we have to have an easy way to write integral constants, but I think this is *not* the way we should go.
Of course, this kind of proposal is easy and can be used in a transient state.
Let me explain what I have in mind:
Every literal (at the grammar level) has a special type for literals. So when you write `1`, you don't write an `int` but write a `std::integral_constant<int, 1>` that you can add to other integral constants. It is automatically and transparently convertible into an `int`.
One last thing for this to work: `auto i = 1;` and `decltype(1)` should deduce `int` and *not* `std::integral_constant<int, 1>` (I think there is a proposal to customize auto deduction for a class, but I don't remember it).
It is also important that this also works when floating points literal (There is some proposal about floats in template arguments) and string literal.
For string literal, I would say it would be the opportunity to string literals not to be `const char*` or `const char (&) [N]` but have a proper type.
We don't have to use specifically `std::integral_constant`, but as we already have a class that has the meaning we want, so why not use it?
Another way to do it even better (in my opinion) would be the ability to have type specifier (like `constexpr`?) telling you if the type is a compile time constant that you could overload on it and use a template parameters even if it comes from a function argument. That way, it would be possible to reduce the number of actual types during compilation, and would also work on user defined types. I think there also are some proposals in that direction.
I think the important thing is the ability to handle literals *transparently* without having to say that a particular expression is a literal.
Whatever direction is chosen, I think it would be good to get rid of the `const char*` or `const char(&)[N]` for string literals. Those are a nightmare to manipulate within meta-programs.
On Friday, September 15, 2017 at 1:17:48 AM UTC-7, floria...@gmail.com wrote:I completely agree that we have to have an easy way to write integral constants, but I think this is *not* the way we should go.
Of course, this kind of proposal is easy and can be used in a transient state.
Let me explain what I have in mind:
Every literal (at the grammar level) has a special type for literals. So when you write `1`, you don't write an `int` but write a `std::integral_constant<int, 1>` that you can add to other integral constants. It is automatically and transparently convertible into an `int`.
One last thing for this to work: `auto i = 1;` and `decltype(1)` should deduce `int` and *not* `std::integral_constant<int, 1>` (I think there is a proposal to customize auto deduction for a class, but I don't remember it).
It is also important that this also works when floating points literal (There is some proposal about floats in template arguments) and string literal.
For string literal, I would say it would be the opportunity to string literals not to be `const char*` or `const char (&) [N]` but have a proper type.Yes, what works for fundamental integers currently should be ready to accept other types as they become permitted as non-type template parameters in future.
We don't have to use specifically `std::integral_constant`, but as we already have a class that has the meaning we want, so why not use it?I am already dissuaded from using `integral_constant`. As Morwenn pointed out, `integral_constant` requires spelling out the type of the constant value whereas `template<auto>` now makes that unnecessary.
Another way to do it even better (in my opinion) would be the ability to have type specifier (like `constexpr`?) telling you if the type is a compile time constant that you could overload on it and use a template parameters even if it comes from a function argument. That way, it would be possible to reduce the number of actual types during compilation, and would also work on user defined types. I think there also are some proposals in that direction.
I think the important thing is the ability to handle literals *transparently* without having to say that a particular expression is a literal.I agree that the less there is a distinction the better. But I'm specifically looking for a mechanism with which to determine the type of objects from a value. Achieving that may be far beyond the scope I'm thinking of.An example of the kind of thing I'd like to do is:auto a = narrowest_int { 120 }; // sizeof(a) == 1auto b = narrowest_int { 10000 }; // sizeof(b) == 2auto c = narrowest_int { i }; // if narrowest_int is deduced from an int variable, there is no way to know how compact c can be and sizeof(int) must be assumed
Whatever direction is chosen, I think it would be good to get rid of the `const char*` or `const char(&)[N]` for string literals. Those are a nightmare to manipulate within meta-programs.That would avoid a lot of inconvenience but it's a language change. I think I can achieve what I'm after with a library addition.
template <auto val>
struct literal { constexpr literal() = default; constexpr literal(const literal&) = default; ~literal() = default;
constexpr operator decltype(val)() const { return val; }
//using auto = decltype(val);};
template <auto V1, auto V2> requires(requires{V1 + V2;})constexpr literal<V1 + V2> operator+(literal<V1>, literal<V2>) { return {}; }// ... other operators
constexpr std::size_t log2ll(std::size_t);constexpr std::size_t next_power2(std::size_t);
template <std::size_t>struct narrower_int;
template <>struct narrower_int<1> { std::int8_t i; using auto = std::int8_t; constexpr operator std::int8_t() const { return i; }};template <>struct narrower_int<2> { std::int16_t i; using auto = std::int16_t; constexpr operator std::int16_t() const { return i; }};// ... other sizes
template <class T> requires(std::is_integral<T>::value)narrower_int(T) -> narrower_int<sizeof(T)>;
template <auto val> requires(std::is_integral<decltype(val)>::value)narrower_int(literal<val>) -> narrower_int<next_power2(1+(1+log2ll(val))/8)>;
// It is also possible to do it with functions