Reputation: 202
Given the two constexpr
functions, is it possible to combine them into one function?
template <char... C>
constexpr int boo()
{
char ch[] = { C... };
int count = 0;
for (char c : ch)
{
if (c != '0') count += 1;
}
return count;
}
template <char... C>
constexpr auto foo()
{
std::array<char, boo<C...>()> x{};
return x;
}
As the example shows I can return 'count
' as a constant.
My problem is I can't use 'count
' as a constant in the function it's declared. That is if the body of 'boo()
' is placed in 'foo()
', the compiler throws up with 'count
' not being a constant.
Upvotes: 2
Views: 229
Reputation: 26066
For C++14 and up, if your goal is to "merge" the bodies, you could simply define a type inside your function template:
template <char... C>
constexpr auto foo()
{
struct {
constexpr int operator()() {
char ch[] = { C... };
int count = 0;
for (char c : ch)
{
if (c != '0') count += 1;
}
return count;
};
} boo;
std::array<char, boo()> x{};
return x;
}
If you have C++17, you can also use lambdas in constant expressions, so you can shorten boo
to:
constexpr auto boo = []() { /* ... */ };
In C++20, you will be able to write lambda expressions directly as a template argument, so you could reduce further to (if you really wanted it):
std::array<char, []() { /* ... */ }()> x{};
Having said that, in general, I would say that the usual (and cleaner) approach to have all kinds of extra code used by templates in a header but that are not part of the public interface is putting them in a detail
or similarly named namespace:
namespace detail {
template <char... C>
constexpr int boo()
{
/* ... */
}
}
template <char... C>
constexpr auto foo()
{
/* ... detail::boo<C...>() ... */
}
Upvotes: 4
Reputation: 66200
The problem is that std::array
needs a constant as size value.
If you define count
and modify it inside foo()
, count
(as seen inside the foo()
function) is a variable, not a constant.
So you need to modify it in another place: in a constexpr
function, so the returned value become a compile-time known constant.
If you can use C++17, so template folding (with improvement from Evg and Rakete1111; thanks), you can avoid bar()
at all
template <char... C>
constexpr auto foo()
{
std::array<char, (0u + ... + (C != '0'))> x{};
return x;
}
but if you have only C++11, you need recursion
template <typename = void>
constexpr std::size_t bar ()
{ return 0u; }
template <char C0, char ... C>
constexpr std::size_t bar ()
{ return bar<C...>() + (C0 == '0' ? 0u : 1u); }
template <char... C>
constexpr std::array<char, bar<C...>()> foo()
{ return {}; }
Upvotes: 5