8

Is it possible to deduce template value (not type) for a c++17 function?

The function foo:

template<int I>
int foo()
{
    return (I);
}

Can be called via:

foo<5>();

And will return 5.

Template types can be deduced through the type of a function argument. Is it possible to do the same in some way for the template value? For example:

template<int I = x>
int bar(const int x)
{
    return (I);
}

This obviously wont work (because for one x is required before its declaration), but might there be some C++17 trick which would allow for this?

I'd like to use this as a way of setting a constant expression function argument.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
user7119460
  • 1,451
  • 10
  • 20
  • Nope. C++ does not work this way. Template parameters are always deduced at compile time. You don't know what parameters a function receives until run time. – Sam Varshavchik May 23 '18 at 03:38
  • Why would you want that? What is the use-case for passing the value both as a template and as an argument? – Some programmer dude May 23 '18 at 03:40
  • 2
    Do note that the "opposite" is possible: `template int bar(const int x = X) { ... }` – Some programmer dude May 23 '18 at 03:41
  • @Someprogrammerdude: "*What is the use-case for passing the value both as a template and as an argument?*" Because C++ does not have `constexpr` *parameters*. But non-type template parameters are always constant expressions. So being able to invoke a (presumably `constexpr` function with a parameter it can treat as `constexpr` would be useful. Until we get real `constexpr` parameters, you have to find a workaround with template parameters. – Nicol Bolas May 23 '18 at 03:48
  • @NicolBolas I know, but I still don't see the point in passing it *both* as a template, *and* as an argument. Other than possibly being able to not use the `` syntax when calling the function. If a constant expression use only the template? Or make the whole function `constexpr` and pass a constant expression argument (if possible)? – Some programmer dude May 23 '18 at 03:54

2 Answers2

6

What you want can only be done by (ab)using type deduction for integer deduction. Observe:

template<int x>
struct integer_value {};

template<int x>
void test(integer_value<x> val)
{
  //x can be used here.
}

Of course, you must invoke this with test(integer_value<4>{}) or something similar.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 2
    FWIW@op, the most plain spelling of this in Boost.Hana is `test(5_c)`. It's possible to get almost the exact call syntax desired. – chris May 23 '18 at 03:52
  • @chris: Sure. But the implementation of `_c`, as I understand it, is decidedly non-trivial. You have to actually parse the `` form of UDL syntax. Unless you have a better solution? – Nicol Bolas May 23 '18 at 04:03
  • Yes, it's unfortunate. I expect one of these constexpr-parameter-like avenues will pan out and give us something better. – chris May 23 '18 at 04:06
3
template<auto x>
using constant = std::integral_constant< std::decay_t<decltype(x)>, x >;

template

using constant = std::integral_constant< std::decay_t<decltype(x)>, x >;
constexpr int digit_val( char c ) {
    if (c >= '0' && c <='9')
        return c-'0';
    else if (c >= 'A' && c <= 'Z')
        return c-'A'+10;
    else if (c >= 'a' && c <= 'z')
        return c-'a'+10;
    else
        throw nullptr;
}
constexpr long long ce_pow( int base, int count, long long acc=1 ){
  if (!count) return acc;
  return ce_pow( base, count-1, acc*(long long)base );
}
constexpr long long from_digits( long long acc, int base ){
  return acc;
}
template<int I, int...Is>
constexpr long long from_digits( long long acc, int base, constant<I>, constant<Is>... ) {
  return from_digits( acc+ce_pow(base, sizeof...(Is))*(long long)I, base, constant<Is>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<'x'>, constant<cs>... ){
  return from_digits( 0, 16, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<'b'>, constant<cs>... ){
  return from_digits( 0, 2, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<cs>... ){
  return from_digits( 0, 8, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr auto operator""_k(){
    return constant< val( constant<cs>{}... ) >{};
}

or somesuch. Now:

template<int I>
int bar(constant<I>)
{
  return (I);
}

shoukd work with bar(5_k);. I may have some typos, and the fancy auto constant template may block deduction, and 0X and 0B support is missing. But other than that is sound.


Alternative loop based implementation:

struct number_format {
    int prefix = 0;
    int base = 0;
};
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'x'>, constant<cs>... ) {
    return {2,16};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'X'>, constant<cs>... ) {
    return {2,16};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'b'>, constant<cs>... ) {
    return {2,2};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'B'>, constant<cs>... ) {
    return {2,2};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<cs>... ) {
    return {1,8};
}
template<char...cs>
constexpr number_format get_format( constant<cs>... ) {
    return {0,10};
}
template<char...Cs>
constexpr long long val( constant<Cs>...cs ){
  char buff[] = {Cs...};
  constexpr number_format fmt = get_format( cs... );

  long long r = 0;
  for (auto it = std::begin(buff)+fmt.prefix; it != std::end(buff); ++it) {
      r *= fmt.base;
      r += digit_val(*it);
  }
  return r;
}
template<char...cs>
constexpr auto operator""_k(){
    return constant< val( constant<cs>{}... ) >{};
}

live examples.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524