Constexpr function parameters through special conversions

141 views
Skip to first unread message

Nicol Bolas

unread,
Apr 26, 2017, 3:29:33 PM4/26/17
to ISO C++ Standard - Future Proposals
Function parameters in `constexpr` functions aren't constexpr; only non-type template parameters are considered constant expressions. However, you can approximate `constexpr` parameters through the use of a type:

template<size_t N>
constexpr auto func(std::integral_constant_t<size_t, N>)
{
 
return std::array<int, N>{};
}

The problem with this is that you have to invoke such a function in a very unnatural way: `func(std::integral_constant<std::size_t, 20>);` That puts a lot of noise in the function argument, when what you really want to say is `func(20);`

What I suggest is making `integral_constant` specially recognized by the compiler in template argument deduction circumstances. If the given argument for such a parameter is either an integer literal (with a non-narrowing conversion to the `integral_constant`'s type) or a constexpr variable (who's type has a non-narrowing conversion to the `integral_constant`'s type), then the compiler will deduce the corresponding `N` as if you had spelled out `std::integral_constant_t<typename>`. If the type of `integral_constant` is also deduced (either via C++17's `auto` or as a template parameter), then this would work as well, simply using the type of the expression.

We could also add more `type_constant` types to the standard library, like `float_constant`, `pointer_constant`, etc.

One reason I bring this up is that this feels like it's really a part of a more generic feature, but I can't think of what such a feature might look like. This looks like some mechanism for customizing how template argument deduction works, but what would that actually be?

John McFarlane

unread,
Apr 26, 2017, 4:17:15 PM4/26/17
to ISO C++ Standard - Future Proposals
I would also like to be able to do more static work with function parameters. For example:

auto x = just_wide_enough(100);  // returns an int8_t
auto y = just_wide_enough(1000);  // returns an int16_t

Note that it is already possible to generate integral_constant from user-defined literals so `func(20_c)` is a workaround, e.g. [http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/namespaceboost_1_1hana_1_1literals.html#a85ac3c47d02722a334181aab540e732c].

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6eb51b1b-d526-486b-9dc2-6ccd4e9f514d%40isocpp.org.

Thomas Köppe

unread,
Apr 26, 2017, 4:23:43 PM4/26/17
to ISO C++ Standard - Future Proposals
A random thought - how about:

    template <auto V> struct c : std::integral_constant<decltype(V), V> {};

Usage:

    func(c(10));  // calls func(std::integral_constant<int, 10>)

(This may require a deduction guide, I'm not sure.)

Zhihao Yuan

unread,
Apr 26, 2017, 4:28:48 PM4/26/17
to std-pr...@isocpp.org
std::integral_constant has no constructor taking
`10` so this doesn't work.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________

Thomas Köppe

unread,
Apr 26, 2017, 4:45:30 PM4/26/17
to ISO C++ Standard - Future Proposals
No, never mind, that doesn't make any sense at all. You could write func(c<10>()) I suppose (no deduction guides required), but it's a bit of a hack. 

Zach Laine

unread,
Apr 26, 2017, 4:53:22 PM4/26/17
to std-pr...@isocpp.org
The way Boost.Hana handles this is with a UDL for its own integral constant type.  So getting the Nth value of a tuple might look like this:

hana::tuple</*...*/> t;
auto second_element = t[1_c];

The tuple's operator[] can therefore be evaluated at compile time (depending on the element types of course).

It might be nice to have something similar for std::integral_constant<T>, though the possibility of several different valid instantiations makes this nontrivial.

Zach


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Zhihao Yuan

unread,
Apr 26, 2017, 5:01:33 PM4/26/17
to std-pr...@isocpp.org
On Wed, Apr 26, 2017 at 12:29 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> template<size_t N>
> constexpr auto func(std::integral_constant_t<size_t, N>)
> {
> return std::array<int, N>{};
> }
>
> The problem with this is that you have to invoke such a function in a very
> unnatural way: `func(std::integral_constant<std::size_t, 20>);` That puts a
> lot of noise in the function argument, when what you really want to say is
> `func(20);`
>
> What I suggest is making `integral_constant` specially recognized by the
> compiler in template argument deduction circumstances.

I have the same thought. I also want valued-type to just
work as well:

template <typename T>
struct type {}; // hana::type

template <typename T>
void f(type<T> x, ...);

f(int); // picked up by overload resolution

Marcelo Zimbres

unread,
Apr 26, 2017, 5:08:15 PM4/26/17
to std-pr...@isocpp.org

Nicol Bolas

unread,
Apr 26, 2017, 8:23:36 PM4/26/17
to ISO C++ Standard - Future Proposals


On Wednesday, April 26, 2017 at 4:17:15 PM UTC-4, John McFarlane wrote:
I would also like to be able to do more static work with function parameters. For example:

auto x = just_wide_enough(100);  // returns an int8_t
auto y = just_wide_enough(1000);  // returns an int16_t

Note that it is already possible to generate integral_constant from user-defined literals so `func(20_c)` is a workaround, e.g. [http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/namespaceboost_1_1hana_1_1literals.html#a85ac3c47d02722a334181aab540e732c].


That's a partial solution. It doesn't allow `constexpr` variables to be used. Nor can it handle narrowing conversions properly.

Chris DeVisser

unread,
Apr 27, 2017, 9:09:02 AM4/27/17
to ISO C++ Standard - Future Proposals

FWIW, @ubsan in the C++ Slack came up with something similar to allow any constexpr-enabled type to be used in this way, using the same technique as Hana's string does, but generalized: https://godbolt.org/g/U6KjKf

In particular:

#define typeval(_value) []() {\
 
struct __anonymous {\
   
using type = decltype(_value);\
   
constexpr static auto value() -> type {\
     
return _value;\
   
}\
 
};\
 
return __anonymous();\
}()

This allows, for example, foo(typeval(2.5)) to have a compile-time-only double parameter. While the if constexpr() proposal is great for all-or-nothing, it doesn't easily support having, say, a compile-time format string and compile-time or runtime value arguments. Perhaps this approach is worth exploring? The question becomes how best to avoid the need for using typeval at the call site and how to make it friendly for the function author to use in the first place.

I'm also interested to see what ideas SG7 has about compile-time-only parameters in general.

Louis Dionne

unread,
Apr 27, 2017, 10:52:31 AM4/27/17
to ISO C++ Standard - Future Proposals
Just for historical accuracy, I believe the technique Chris mentions was first introduced by Michael Park in 2014 in his `format` library: https://github.com/mpark/format. I remember talking to him about this at CppCon that year and then deriving Hana's `BOOST_HANA_STRING` macro (for compile-time strings) from it.

As for the general feature, I believe solving this problem is equivalent to allow passing arbitrary constexpr types as non-type template parameters. Indeed, given such a feature, we could simply say that a `constexpr` parameter is equivalent to passing it as a non-type template parameter, with syntactic sugar. However, implementing this is highly non-trivial, as it requires the ability to determine when two non-type template arguments are "equal", which could be very involved for user-defined types. Of course, a partial solution that allows only a subset of user defined types might also be enough.

Louis

Edward Catmur

unread,
Apr 27, 2017, 5:15:15 PM4/27/17
to ISO C++ Standard - Future Proposals
However with variable templates we can write:

template<auto I> auto const_v =
std::integral_constant<decltype(I), I>{};
func(const_v<20u>);

I've started using this technique quite a lot in metaprogramming recently.
Reply all
Reply to author
Forward
0 new messages