Reputation: 906
say you have a function like:
double do_it(int m)
{
double result = 0;
for(int i = 0; i < m; i++)
result += i;
return result;
}
If you know m at compile time you can do:
template<size_t t_m>
double do_it()
{
double result = 0;
for(int i = 0; i < t_m; i++)
result += i;
return result;
}
This gives a possibility for things like loop unrolling when optimizing. But, sometimes you might know some cases at compile-time and some at run-time. Or, perhaps you have defaults which a user could change...but it would be nice to optimize the default case.
I'm wondering if there is any way to provide both versions without basically duplicating the code or using a macro?
Note that the above is a toy example to illustrate the point.
Upvotes: 4
Views: 193
Reputation: 97571
Use constexpr
(needs at least C++14 to allow for
):
constexpr double do_it(int m)
{
double result = 0;
for(int i = 0; i < m; i++)
result += i;
return result;
}
constexpr double it_result = do_it(10) + 1; // compile time `do_it`, possibly runtime `+ 1`
int main() {
int x;
cin >> x;
do_it(x); // runtime
}
If you want to force a constexpr value to be inlined as part of a runtime expression, you can use the FORCE_CT_EVAL
macro from this comment:
#include <utility>
#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
double it_result = FORCE_CT_EVAL(do_it(10)); // compile time
Upvotes: 1
Reputation: 884
Yes you can, with std::integral_constant
. Specifically, the following function will work with an int
, as well as specializations of std::integral_constant
.
template<class Num>
constexpr double do_it(Num m_unconverted) {
double result = 0.;
int m_converted = static_cast<int>(m_unconverted);
for(int i = 0; i < m_converted; i++){ result += i; }
return result;
}
If you want to call do_it
with a compile-time constant, then you can use
constexpr double result = do_it(std::integral_constant<int, 5>{});
Otherwise, it's just
double result = do_it(some_number);
Upvotes: 3
Reputation: 473407
In terms of the language specification, there's no general way to have a function that works in the way you desire. But that doesn't mean compilers can't do it for you.
This gives a possibility for things like loop unrolling when optimizing.
You say this as though the compiler cannot unroll the loop otherwise.
The reason the compiler can unroll the template loop is because of the confluence of the following:
The compiler has the definition of the function. In this case, the function definition is provided (it's a template function, so its definition has to be provided).
The compiler has the compile-time value of the loop counter. In this case, through the template parameter.
But none of these factors explicitly require a template. If the compiler has the definition of a function, and it can determine the compile-time value of the loop counter, then it has 100% of the information needed to unroll that loop.
How it gets this information is irrelevant. It could be an inline
function (you have to provide the definition) which you call given a compile-time constant as an argument. It could be a constexpr
function (again, you have to provide the definition) which you call given a compile-time constant as an argument.
This is a matter of quality of implementation, not of language. If compile-time parameters are to ever be a thing, it would be to support things you cannot do otherwise, not to support optimization (or at least, not compiler optimizations). For example, you can't have a function which returns a std::array
whose length is specified by a regular function parameter rather than a template parameter.
Upvotes: 5