Reputation: 597
Is there a way to overload a function in a way to distinguish between the argument being evaluable at compile time or at runtime only?
Suppose I have the following function:
std::string lookup(int x) {
return table<x>::value;
}
which allows me to select a string value based on the parameter x in constant time (with space overhead). However, in some cases x
cannot be provided at compile time, and I need to run a version of foo which does the lookup with a higher time complexity.
I could use functions with a different name of course, but I would like to have an unified interface.
I accepted an answer, but I'm still interested if this distinction is possible with exactly the same function call.
Upvotes: 4
Views: 292
Reputation: 1469
There is also this trick:
std::string lookup(int x) {
switch(x) {
case 0: return table<0>::value;
case 1: return table<1>::value;
case 2: return table<2>::value;
case 3: return table<3>::value;
default: return generic_lookup(x);
}
This sort of thing works well when it's advantageous, but not required, for the integer to be known at compile time. For example, if it helps the optimizer. It can be hell on compile times though, if you're calling many instances of some complicated function in this way.
Upvotes: 0
Reputation: 62975
I believe the closest you can get is to overload lookup
on int
and std::integral_constant<int>
; then, if the caller knows the value at compile-type, they can call the latter overload:
#include <type_traits>
#include <string>
std::string lookup(int const& x) // a
{
return "a"; // high-complexity lookup using x
}
template<int x>
std::string lookup(std::integral_constant<int, x>) // b
{
return "b"; // return table<x>::value;
}
template<typename T = void>
void lookup(int const&&) // c
{
static_assert(
!std::is_same<T, T>{},
"to pass a compile-time constant to lookup, pass"
" an instance of std::integral_constant<int>"
);
}
template<int N>
using int_ = std::integral_constant<int, N>;
int main()
{
int x = 3;
int const y = 3;
constexpr int z = 3;
lookup(x); // calls a
lookup(y); // calls a
lookup(z); // calls a
lookup(int_<3>{}); // calls b
lookup(3); // calls c, compile-time error
}
Notes:
int_
helper here so construction of std::integral_constant<int>
is less verbose for the caller; this is optional.constexpr int
variables are passed to overload a, not overload c), but this will weed out any actual int literals.Upvotes: 2
Reputation: 62593
One option would be to use overloading in a similar manner:
template <int x> std::string find() {
return table<x>::value;
}
std::string find(int x) {
return ...
}
Upvotes: 2