Implicit constexpr parameter to template parameter conversion.

182 views
Skip to first unread message

Nicol Bolas

unread,
Sep 10, 2016, 10:56:04 AM9/10/16
to ISO C++ Standard - Future Proposals
Consider the following code:

template<typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl, int index)
{
 
return std::get<index>(std::forward(tpl));
}

This is, of course, not legal. And for good reason: the return value of this function would have to be able to change based on the parameter you pass. That's very un-function-like behavior.

But we do have a way to do that. We instead make `index` a non-type template parameter:

template<int index, typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl)
{
 
return std::get<index>(std::forward(tpl));
}

However, this forces us to call the function via `get_tuple<index>(tpl)`. And while this obviously works, it's sub-optimal visually.

So I suggest making the following possible:

template<typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl, constexpr int index)
{
 
return std::get<index>(std::forward(tpl));
}

So, how does this work?

It works by two transformations. First, that declaration is implicitly transformed (in a manner similar to Concepts TS's use of `auto` and Concept parameter types) into adding a template parameter. However, on the surface, it's still a function with two parameters.

Second, when the user calls the function, any arguments provided to a `constexpr` parameter are implicitly transformed into template arguments rather than function arguments. Function overloading and so forth behave as normal (though `constexpr` parameters should probably score higher if the argument provided is a constant expression. And obviously if you provide an argument that is not a constant expression, and no other overload that could work exists, you get a compiler error.

Barry Revzin

unread,
Sep 10, 2016, 1:46:00 PM9/10/16
to ISO C++ Standard - Future Proposals


template<typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl, constexpr int index)
{
 
return std::get<index>(std::forward(tpl));
}


Why not just:

template <int I>
using int_ = std::integral_constant<int, I>;

template <typename... Ts, int I>
auto get_tuple(std::tuple<Ts...> tpl, int_<I> )
{
   
return std::get<I>(tpl);
}

get_tuple
(tpl, int_<index>{} );

 Works today, and lets you put your arguments in the order you want. 

Nicol Bolas

unread,
Sep 10, 2016, 2:07:34 PM9/10/16
to ISO C++ Standard - Future Proposals

The whole point of the idea is to allow you to use `get_tuple(tpl, 1)`. Or `get_tuple(tpl, some_constexpr_value)`. If the caller has to know that you're passing a template parameter, then you've failed. If the function using the parameter has to treat it in any way differently from any other constexpr variable, then you've failed.

I'm trying to solve the problem which is at the core of the hack-fix you've suggested.

Furthermore, the real solution would be more general, extensible to any type which can be a non-type template parameter. For example, there's an idea of extending non-type template parameters to any literal type. My idea would instantly mesh with that. Yours would require a generalized form of `integral_constant`.

Matt Calabrese

unread,
Sep 12, 2016, 5:46:40 PM9/12/16
to ISO C++ Standard - Future Proposals
On Sat, Sep 10, 2016 at 7:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:
Consider the following code:

template<typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl, int index)
{
 
return std::get<index>(std::forward(tpl));
}

This is, of course, not legal. And for good reason: the return value of this function would have to be able to change based on the parameter you pass. That's very un-function-like behavior.

But we do have a way to do that. We instead make `index` a non-type template parameter:

template<int index, typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl)
{
 
return std::get<index>(std::forward(tpl));
}

However, this forces us to call the function via `get_tuple<index>(tpl)`. And while this obviously works, it's sub-optimal visually.

So I suggest making the following possible:

template<typename ...Ts>
auto get_tuple(std::tuple<Ts...> &&tpl, constexpr int index)
{
 
return std::get<index>(std::forward(tpl));
}

I remember from various conferences that this has been desired/requested even since before C++11 was finalized -- a certain population in the C++ community would very much like to see *something* akin to this, myself included. I wasn't participating in the committee back then and I don't know the exact rationale for why it wasn't pursued, though it could end up getting hairy once you get into the details of it. How would this play with overload resolution (the existence of ADL might make this a question that needs to be addressed somehow rather than strictly disallowed)? Would it be possible to somehow perfect-forward to such a function? Would it be possible to form a pointer or reference to a specific instantiation of such a function? There are certainly answers to these questions, though they are not necessarily obvious. It might also just be that there hasn't been a proposal that hammered out the details, in which case I'd encourage you to write such a paper.

Another thing to consider -- using constexpr in the parameter list seems to somewhat imply an implicit template, which a lot of people don't like. By that I mean each different int value in your example would imply a different function being instantiated, whether we call it a template or not. Maybe modules will change people's feelings about that, though.

Anyway, please investigate further. I would very much like to see a proposal for something like this, especially if it would allow me to overload as follows:

void foo( int );
void foo( constexpr int );

I have a feeling that it would be very difficult for this to get through committee, though.

Richard Smith

unread,
Sep 12, 2016, 5:54:19 PM9/12/16
to std-pr...@isocpp.org
If you go forward with a paper on this, I'd like to see discussion of examples like the following:

int *f(constexpr int n) {
  static int k[n];
  return &k;
}

Do f(0) and f(1) return the same pointer? Likewise:

auto g(constexpr int n) {
  struct S { int arr[n]; };
  return S{};
}

Do g(0) and g(1) have the same return type? Do these answers change if 'n' is not used in the formation of the type?

And here:

template<int n> struct X { template<typename T> struct Y {}; };
void h(constexpr int n) {
  X<n>::Y<int> xy; // 'template' keyword required here?
}

Essentially, the question is: to what extent are these functions-with-constexpr-parameters like function templates, and to what extent are they like regular functions?

Nicol Bolas

unread,
Sep 12, 2016, 7:59:44 PM9/12/16
to ISO C++ Standard - Future Proposals
On Monday, September 12, 2016 at 5:54:19 PM UTC-4, Richard Smith wrote:
Essentially, the question is: to what extent are these functions-with-constexpr-parameters like function templates, and to what extent are they like regular functions?

They should be in every way like template functions. That's the basis of the idea: it's pure syntactic sugar.

Though I realize that it's probably better for the transformation to be from this:

void f(int x, constexpr int y);

to this:

template<int y> void f(int x, int = int());

This way, the declaration looks like a function of two parameters, so the transformation should be as well. But if you call it without specifying that template parameter, then template argument deduction takes over and hijacks it. If you specify the template parameter directly, then the second parameter is ignored by the implementation.

Richard Smith

unread,
Sep 12, 2016, 8:44:36 PM9/12/16
to std-pr...@isocpp.org
On Mon, Sep 12, 2016 at 4:59 PM, Nicol Bolas <jmck...@gmail.com> wrote:
On Monday, September 12, 2016 at 5:54:19 PM UTC-4, Richard Smith wrote:
Essentially, the question is: to what extent are these functions-with-constexpr-parameters like function templates, and to what extent are they like regular functions?

They should be in every way like template functions. That's the basis of the idea: it's pure syntactic sugar.

I expect you'll see some resistance over this. There's already been resistance to the concepts TS for the same reason ('auto' parameters implicitly turning functions into function templates, and particularly concept-names doing the same with no syntactic marker at all). But we'll see.

Do you intend to relax the restrictions on non-type template parameter types for this? That would reopen a can of worms...

Though I realize that it's probably better for the transformation to be from this:

void f(int x, constexpr int y);

to this:

template<int y> void f(int x, int = int());

This way, the declaration looks like a function of two parameters, so the transformation should be as well. But if you call it without specifying that template parameter, then template argument deduction takes over and hijacks it. If you specify the template parameter directly, then the second parameter is ignored by the implementation.

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/53c0c762-b39a-4b99-8a68-68d7591cbc87%40isocpp.org.

Nicol Bolas

unread,
Sep 13, 2016, 1:44:01 AM9/13/16
to ISO C++ Standard - Future Proposals


On Monday, September 12, 2016 at 8:44:36 PM UTC-4, Richard Smith wrote:
On Mon, Sep 12, 2016 at 4:59 PM, Nicol Bolas <jmck...@gmail.com> wrote:
On Monday, September 12, 2016 at 5:54:19 PM UTC-4, Richard Smith wrote:
Essentially, the question is: to what extent are these functions-with-constexpr-parameters like function templates, and to what extent are they like regular functions?

They should be in every way like template functions. That's the basis of the idea: it's pure syntactic sugar.

I expect you'll see some resistance over this. There's already been resistance to the concepts TS for the same reason ('auto' parameters implicitly turning functions into function templates, and particularly concept-names doing the same with no syntactic marker at all). But we'll see.

There's already a thread about providing a specific syntax for terse templates, presumably in reaction to these concerns. My idea can piggy back off of that just as effectively as it does off of Concepts TS's terse templates.

I don't mind if I have to write `template<> void foo(constexpr int x);`. What matters is that when I call it, I can use `foo(5)` rather than `foo<5>()`.

Sure, if neither happens and there's no template form that implicitly adds template arguments from a parameter list, then obviously this form of that wouldn't be allowed either.

Do you intend to relax the restrictions on non-type template parameter types for this? That would reopen a can of worms...

The idea is merely a syntactic transform. Therefore, the limits of the final form are what define the limits on the initial form. If we expand the permitted types for non-type template parameters, then they'll work just as well with this syntax.

inkwizyt...@gmail.com

unread,
Sep 13, 2016, 8:35:11 AM9/13/16
to ISO C++ Standard - Future Proposals

I think better would be use of UDL:

template<int I>
int foo(std::value_t<I>, int b)
{
 
static int tab[I] = { };
 
return tab[b]++;
}
template<int I>
void bar(std::value_t<I> a)
{
   
auto t = 15v + 5v * a; // or `std::value_v<15 + 5 * I>`.
    foo
(t, a + 10); //type of second arg is `int`.
}

int main()
{
   
auto x = foo(10v, 5);
    bar
(3v);
}


Giovanni Piero Deretta

unread,
Sep 13, 2016, 8:44:46 AM9/13/16
to ISO C++ Standard - Future Proposals
On Tuesday, September 13, 2016 at 12:59:44 AM UTC+1, Nicol Bolas wrote:
On Monday, September 12, 2016 at 5:54:19 PM UTC-4, Richard Smith wrote:
Essentially, the question is: to what extent are these functions-with-constexpr-parameters like function templates, and to what extent are they like regular functions?

They should be in every way like template functions. That's the basis of the idea: it's pure syntactic sugar.


The idea is nice, the problem is that only a small subset of literal types can be used as non-type template parameters. As generalizing non type template parameters to any literal type has been shot down multiple times by the committee, defining this feature as pure syntactic sugar would hinder further evolution.

-- gpd

Nicol Bolas

unread,
Sep 13, 2016, 10:14:58 AM9/13/16
to ISO C++ Standard - Future Proposals

But that's the only way to make a constexpr parameter work. The behavior of a function, including the static types it uses, can change depending on the constexpr parameter it uses. That's the whole point. And doing that requires either invoking template machinery or defining a whole new kind of template-like construct.

So whatever reasons the committee had for preventing the use of general literal types as non-type template parameters would apply just as well for `constexpr` parameters.

Arthur O'Dwyer

unread,
Sep 13, 2016, 8:32:47 PM9/13/16
to ISO C++ Standard - Future Proposals
For the record:
For a type to be useable as a constexpr parameter (i.e. to be a literal type), the compiler must be able to manipulate values of the type at compile-time.
For a type to be useable as a non-type template parameter, the type must support identity-comparison in some meaningful sense, and also, the compiler must be able to stringify values of the type at compile-time (in a whole-program-consistent 1:1 way such that identity-equal values stringify to bytewise-equal strings).

That's so that we can name-mangle foo<MyFirstValue> differently from foo<MySecondValue>, and also so that we can call foo<MyFirstValue> from a translation unit where it's declared-but-not-defined, define it in another separately compiled translation unit, and still get the linker to resolve the reference correctly.

Notice I'm saying "identity-comparison" instead of "equality-comparison" or "operator=="; for example you can use references as non-type template parameters, even though operator== for references does something different from identity-comparison.  Whereas, type double supports operator== but does not cleanly support identity-comparison, thanks to negative zero and the multitude of NaN values; so it's not allowed as a non-type template parameter.

Tough problems like "is type T stringifiable" and "what does identity comparison mean for type T" are what's holding up string-literal template parameters, among other things.  I naively imagine that most of the committee is sympathetic to the end goal of literal values as non-type template parameters; but that doesn't magically make it implementable. There's still the tough problems to solve, first.

–Arthur
Reply all
Reply to author
Forward
0 new messages