Kaleb Barrett
Kaleb Barrett

Reputation: 1594

Template parameters that require deduced argument *before* user arguments

I have a function where I'd like the arguments partially-deduced and the remaining, mostly non-type arguments, are given by the user (as a way of enforcing them being given at compile-time). However, the type of this non-type user-sourced argument is deduced, therefor it must come before the user arguments. That breaks the ability of the user to have the first argument deduced. I have a small example below, that does not compile, demonstrating what I'm talking about.

template <typename T, T N>
class int_const {};

template <typename T, T M, T N>
auto add(int_const<T, N> a) {
    return int_const<T, N + M>();
}

int main(void) {
    int_const<int, 1> a;
    add<32>(a); 
    // add<int, 32>(a); does compile, but requires the user to know or extract that first argument
    return 0;
}

Is there anyway to support the template function call as it is seen in main()?

Upvotes: 1

Views: 77

Answers (1)

max66
max66

Reputation: 66230

If you can use C++17, you can use auto for M value

template <auto M, typename T, T N>
auto add(int_const<T, N> a) {
    return int_const<T, N + M>();
}

So you can call it as follows

add<32>(a);

Before C++17... well, I don't see a way without explicating also the type.

As pointed by Jarod42, auto M intercept also values of different types.

If you want impose that the type of M is exactly T, you can use SFINAE; by example, as follows

template <auto M, typename T, T N, 
          std::enable_if_t<std::is_same_v<T, decltype(M)>, bool> = true>
auto add(int_const<T, N> a) {
    return int_const<T, N + M>();
}

So you get errors from

add<32u>(a);
add<short{32}>(a);

But maybe you can relax the requirement and accept also that decltype(M) isn't exactly T but also simply that M is narrowing convertible to T.

Maybe

template <auto M, typename T, T N>
auto add(int_const<T, N> a) {
    return int_const<T, N + T{M}>();
} // .......................^^^^

so

add<32u>(a);
add<short{32}>(a);

are compiled because 32u and short{32} are narrowing convertible to int where

add<(unsigned long)(-1)>(a);

gives a compilation error because (unsigned long)(-1) (usually the bigger possible value for an unsigned long) can't be narrowed to int.

Upvotes: 2

Related Questions