I'm pretty new to C++ template programming. I'd like to design a function element such as e.g.
element<3, 3, 3, 3, 3> will return 3
element<3, 3, 2> will fail an assertion
#include <iostream>
#include <cstdlib>
namespace meta
{
template<typename T>
constexpr T element(T x)
{
return x;
}
template<typename T, typename... Ts>
constexpr T element(T x, Ts... xs)
{
constexpr T oth = element(xs...); // $C$
static_assert(oth == x, "element Mismatch");
return x;
}
template<int... DIMS>
void get_elements()
{
std::cout << "elements " << element(DIMS...); // $A$
}
}
int main(int argc, char ** argv)
{
meta::get_elements<2, 3, 4>(); // $B$
static constexpr int D1 = 3, D2 = 3;
meta::get_elements<D1, D2>();
}
But GCC with std=c++14 is failing with
In instantiation of ‘constexpr T meta::element(T, Ts ...) [with T = int; Ts = {int, int}]’:
$A$: required from ‘void meta::get_elements() [with int ...DIMS = {2, 3, 4}]’
$B$: required from here
$C$: error: ‘xs#0’ is not a constant expression
$C$: error: ‘xs#1’ is not a constant expression
I'd like to exploit recursion to perform an equality check on each template argument in the list, and return one of them if they're all equal.
parameters are not constexpr.
You might use std::integral_constant to bypass that
namespace meta
{
template<typename T, T V>
constexpr std::integral_constant<T, V> element(std::integral_constant<T, V>)
{
return {};
}
template<typename T, T V, typename... Ts>
constexpr std::integral_constant<T, V> element(std::integral_constant<T, V> x, Ts... xs)
{
constexpr auto oth = element(xs...); // $C$
static_assert(oth() == x(), "element Mismatch");
return {};
}
template<int... DIMS>
void get_elements()
{
std::cout << "elements " << element(std::integral_constant<int, DIMS>{}...); // $A$
}
}
Here is a simple solution in C++17:
template <int Head, int... Tail> struct element
{
static_assert((... && (Head == Tail)), "missmatch elements");
static constexpr auto value = Head;
};
template <int... I> constexpr auto element_v = element<I...>::value;
auto test()
{
// element_v<3, 3, 1>; // assert fail
constexpr int A = 3, B = 3;
return element_v<3, 3, A, B>;
}
See it on godbolt
Or pre-c++17 (no recursion):
#include <iostream>
#include <cstdlib>
#include <type_traits>
namespace meta
{
template <bool...>
struct boolpack { };
template <bool... Bs>
struct all_of: std::is_same<boolpack<Bs..., true>, boolpack<true, Bs...>> { };
template<int FIRST, int... DIMS>
constexpr void get_elements()
{
static_assert(all_of<DIMS==FIRST...>::value, "!");
std::cout << "elements " << FIRST;
}
}
int main(int argc, char ** argv)
{
static constexpr int D1 = 3, D2 = 3;
meta::get_elements<D1, D2>();
//meta::get_elements<D1, D2, 2>(); //fail
}
[see it live]
Edited: all_of inspired by Jarod42.
Related
I have a function that computes a certain object from a given parameter (say, an important node from a graph). Now, when calculating such an object, the function might allocate some memory. Sometimes I want the function to just return the result, and sometimes to return the result plus the memory used to compute it.
I typically solve this binary case like this:
enum class what {
what1, // return, e.g., just an int
what2 // return, e.g., a std::pair<int, std::vector<int>>
};
template <what w>
std::conditional_t<w == what::what1, int, std::pair<int, std::vector<int>>>
calculate_something(const param& p) { ... }
I would like to generalize the solution above to longer enumerations
enum class list_whats {
what1,
what2,
what3,
what4,
what5
};
One possible solution is to nest as many std::conditional_t as needed
template <list_whats what>
std::conditional_t<
what == list_whats::what1,
int,
std::conditional_t<
what == list_whats::what2,
float,
....
>
>
>
calculate_something(const param& p) { ... }
But this is cumbersome and perhaps not too elegant.
Does anyone know how to do this in C++ 17?
EDIT
To make the question perfectly clear: how do I implement the function return_something so as to be able to run the following main?
int main() {
int s1 = return_something<list_whats::what1>();
s1 = 3;
float s2 = return_something<list_whats::what2>();
s2 = 4.0f;
double s3 = return_something<list_whats::what3>();
s3 = 9.0;
std::string s4 = return_something<list_whats::what4>();
s4 = "qwer";
std::vector<int> s5 = return_something<list_whats::what5>();
s5[3] = 25;
}
I don't think you should use std::conditional at all to solve your problem. If I get this right, you want to use a template parameter to tell your function what to return. The elegant way to do this could look something like this:
#include <vector>
enum class what { what1, what2 };
template <what W>
auto compute() {
if constexpr (W == what::what1) {
return 100;
}
if constexpr (W == what::what2) {
return std::pair{100, std::vector<int>{}};
}
}
auto main() -> int {
[[maybe_unused]] const auto as_int = compute<what::what1>();
[[maybe_unused]] const auto as_pair = compute<what::what2>();
}
You can also use template specialization if you prefer another syntax:
template <what W>
auto compute();
template <>
auto compute<what::what1>() {
return 100;
}
template <>
auto compute<what::what2>() {
return std::pair{100, std::vector<int>{}};
}
Here's my approach:
what_pair is a pair that corresponds one enum to one type.
what_type_index accepts a enum and a std::tuple<what_pair<...>...> and searches the tuple map where the enums are equal and returns index. It returns maximum std::size_t value, if no match was found.
what_type is the final type, it is the tuple element at the found position. The program won't compile when the index is std::size_t max value because of invalid std::tuple access.
template<what W, typename T>
struct what_pair {
constexpr static what w = W;
using type = T;
};
template<what w, typename tuple_map>
constexpr auto what_type_index() {
std::size_t index = std::numeric_limits<std::size_t>::max();
auto search_map = [&]<std::size_t... Ints>(std::index_sequence<Ints...>) {
((std::tuple_element_t<Ints, tuple_map>::w == w ? (index = Ints) : 0), ...);
};
search_map(std::make_index_sequence<std::tuple_size_v<tuple_map>>());
return index;
}
template<what w, typename tuple_map>
using what_type = typename
std::tuple_element_t<what_type_index<w, tuple_map>(), tuple_map>::type;
and this is the example usage:
int main() {
using what_map = std::tuple<
what_pair<what::what1, int>,
what_pair<what::what2, float>,
what_pair<what::what3, double>,
what_pair<what::what4, std::string>,
what_pair<what::what5, std::vector<int>>>;
static_assert(std::is_same_v<what_type<what::what1, what_map>, int>);
static_assert(std::is_same_v<what_type<what::what2, what_map>, float>);
static_assert(std::is_same_v<what_type<what::what3, what_map>, double>);
static_assert(std::is_same_v<what_type<what::what4, what_map>, std::string>);
static_assert(std::is_same_v<what_type<what::what5, what_map>, std::vector<int>>);
//compilation error, because 'what6' wasn't specified in the 'what_map'
using error = what_type<what::what6, what_map>;
}
try it out on godbolt.
I found a possible solution that nests two structs: the first takes a list of Boolean values to indicate which type should be used, and the nested struct takes the list of possible types (see conditional_list in the example code below -- the nested structs were inspired by this answer). But perhaps it's not elegant enough. I'm wondering if there is a possible solution of the form
std::conditional_list<
..., // list of Boolean values, (of any length!)
... // list of types (list that should be as long as the first)
>::type
My proposal
#include <type_traits>
#include <iostream>
#include <vector>
// -----------------------------------------------------------------------------
template<auto A, auto... ARGS>
constexpr auto sum = A + sum<ARGS...>;
template<auto A>
constexpr auto sum<A> = A;
// -----------------------------------------------------------------------------
template <bool... values>
static constexpr bool exactly_one_v = sum<values...> == 1;
// -----------------------------------------------------------------------------
template <bool... values>
struct which {
static_assert(exactly_one_v<values...>);
template <std::size_t idx, bool v1, bool... vs>
struct _which_impl {
static constexpr std::size_t value =
(v1 ? idx : _which_impl<idx + 1, vs...>::value);
};
template <std::size_t idx, bool v>
struct _which_impl<idx, v> {
static constexpr std::size_t value = (v ? idx : idx + 1);
};
static constexpr std::size_t value = _which_impl<0, values...>::value;
};
template <bool... conds>
static constexpr std::size_t which_v = which<conds...>::value;
// -----------------------------------------------------------------------------
template <std::size_t ith_idx, typename... Ts>
struct ith_type {
template <std::size_t cur_idx, typename t1, typename... ts>
struct _ith_type_impl {
typedef
std::conditional_t<
ith_idx == cur_idx,
t1,
typename _ith_type_impl<cur_idx + 1, ts...>::type
>
type;
};
template <std::size_t cur_idx, typename t1>
struct _ith_type_impl<cur_idx, t1> {
typedef
std::conditional_t<ith_idx == cur_idx, t1, std::nullptr_t>
type;
};
static_assert(ith_idx < sizeof...(Ts));
typedef typename _ith_type_impl<0, Ts...>::type type;
};
template <std::size_t ith_idx, typename... ts>
using ith_type_t = typename ith_type<ith_idx, ts...>::type;
// -----------------------------------------------------------------------------
template <bool... conds>
struct conditional_list {
template <typename... ts>
struct good_type {
static_assert(sizeof...(conds) == sizeof...(ts));
typedef ith_type_t<which_v<conds...>, ts...> type;
};
};
// -----------------------------------------------------------------------------
enum class list_whats {
what1,
what2,
what3,
what4,
what5,
};
template <list_whats what>
typename conditional_list<
what == list_whats::what1,
what == list_whats::what2,
what == list_whats::what3,
what == list_whats::what4,
what == list_whats::what5
>::template good_type<
int,
float,
double,
std::string,
std::vector<int>
>::type
return_something() noexcept {
if constexpr (what == list_whats::what1) { return 1; }
if constexpr (what == list_whats::what2) { return 2.0f; }
if constexpr (what == list_whats::what3) { return 3.0; }
if constexpr (what == list_whats::what4) { return "42"; }
if constexpr (what == list_whats::what5) { return {1,2,3,4,5}; }
}
int main() {
auto s1 = return_something<list_whats::what1>();
s1 = 3;
auto s2 = return_something<list_whats::what2>();
s2 = 4.0f;
auto s3 = return_something<list_whats::what3>();
s3 = 9.0;
auto s4 = return_something<list_whats::what4>();
s4 = "qwer";
auto s5 = return_something<list_whats::what5>();
s5[3] = 25;
}
An alternative to working only with types is to write a function that returns the specific type, or an identity<type>. It's sometimes more readable. Here is an example:
// if you don't have it in std
template<typename T>
struct identity {
using type = T;
};
enum class what {
what1,
what2,
what3
};
template<what w>
auto return_type_for_calc() {
if constexpr (w == what::what1) {
return identity<int>();
} else if constexpr (w==what::what2) {
return identity<double>();
} else {
return identity<float>();
}
}
template<what w>
decltype(return_type_for_calc<w>())
calculate_something()
{
return {};
}
int main() {
calculate_something<what::what1>();
calculate_something<what::what2>();
return 0;
}
Although I already posted an answer to my own question, and I accepted an answer from another user, I thought I could post another possibility in tackling this problem using the following struct
template <typename... Ts> struct type_sequence { };
which I learnt about in this talk by Andrei Alexandrescu. Since I learnt quite a bit by using it and the result is a bit simpler than the original answer that used two nested structs I thought I would share it here. However, the solution I would actually implement is the one that was accepted.
This is the full code with a main function included. Notice the change in the specification of function return_something. Now this function indicates the return type (which I like very much, perhaps I'm old fashioned) in a more readable way than in my first answer. You can try it out here.
#include <type_traits>
#include <iostream>
#include <vector>
template <bool... values>
struct which {
template <std::size_t idx, bool v1, bool... vs>
struct _which_impl {
static constexpr std::size_t value =
(v1 ? idx : _which_impl<idx + 1, vs...>::value);
};
template <std::size_t idx, bool v>
struct _which_impl<idx, v> {
static constexpr std::size_t value = (v ? idx : idx + 1);
};
static constexpr std::size_t value = _which_impl<0, values...>::value;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type {
template <std::size_t cur_idx, typename t1, typename... ts>
struct _ith_type_impl {
using type =
std::conditional_t<
ith_idx == cur_idx,
t1,
typename _ith_type_impl<cur_idx + 1, ts...>::type
>;
};
template <std::size_t cur_idx, typename t1>
struct _ith_type_impl<cur_idx, t1> {
using type = std::conditional_t<ith_idx == cur_idx, t1, std::nullptr_t>;
};
using type = typename _ith_type_impl<0, Ts...>::type;
};
template <std::size_t ith_idx, typename... ts>
using ith_type_t = typename ith_type<ith_idx, ts...>::type;
template <bool... conds>
static constexpr std::size_t which_v = which<conds...>::value;
template <typename... Ts> struct type_sequence { };
template <bool... values> struct bool_sequence {
static constexpr std::size_t which = which_v<values...>;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type<ith_idx, type_sequence<Ts...>> : ith_type<ith_idx, Ts...>
{ };
template <typename bool_sequence, typename type_sequence>
struct conditional_list {
using type = ith_type_t<bool_sequence::which, type_sequence>;
};
template <typename bool_sequence, typename type_sequence>
using conditional_list_t =
typename conditional_list<bool_sequence, type_sequence>::type;
enum class list_whats {
what1,
what2,
what3,
what4,
what5,
};
template <list_whats what>
conditional_list_t<
bool_sequence<
what == list_whats::what1,
what == list_whats::what2,
what == list_whats::what3,
what == list_whats::what4,
what == list_whats::what5
>,
type_sequence<
int,
float,
double,
std::string,
std::vector<int>
>
>
return_something() noexcept {
if constexpr (what == list_whats::what1) { return 1; }
if constexpr (what == list_whats::what2) { return 2.0f; }
if constexpr (what == list_whats::what3) { return 3.0; }
if constexpr (what == list_whats::what4) { return "42"; }
if constexpr (what == list_whats::what5) { return {1,2,3,4,5}; }
}
int main() {
[[maybe_unused]] auto s1 = return_something<list_whats::what1>();
[[maybe_unused]] auto s2 = return_something<list_whats::what2>();
[[maybe_unused]] auto s3 = return_something<list_whats::what3>();
[[maybe_unused]] auto s4 = return_something<list_whats::what4>();
[[maybe_unused]] auto s5 = return_something<list_whats::what5>();
}
I've got a parameter pack saved as a tuple in some function traits struct.
How can I find out, how many of those parameters are std::optional types?
I tried to write a function to check each argument with a fold expression, but this doesn't work as I only pass a single template type which is the tuple itself.
void foo1(){}
void foo2(int,float){}
void foo3(int, std::optional<int>, float, std::optional<int>){}
void foo4(int, std::optional<int>, bool){}
template<typename R, typename... TArgs>
struct ftraits<R(TArgs...)>
{
using ret = R;
using args = std::tuple<TArgs...>;
};
template<typename T>
struct is_optional : std::false_type
{
};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type
{
};
template<typename... Ts>
constexpr auto optional_count() -> std::size_t
{
// doesn't work since Ts is a single parameter with std::tuple<...>
return (0 + ... + (is_optional<Ts>::value ? 1 : 0));
}
int main() {
using t1 = typename ftraits<decltype(foo1)>::args;
std::cout << optional_count<t1>() << std::endl; // should print 0
using t2 = typename ftraits<decltype(foo2)>::args;
std::cout << optional_count<t2>() << std::endl; // should print 0
using t3 = typename ftraits<decltype(foo3)>::args;
std::cout << optional_count<t3>() << std::endl; // should print 2
using t4 = typename ftraits<decltype(foo4)>::args;
std::cout << optional_count<t4>() << std::endl; // should print 1
}
You can use template partial specialization to get element types of the tuple and reuse the fold-expression
template<typename>
struct optional_count_impl;
template<typename... Ts>
struct optional_count_impl<std::tuple<Ts...>> {
constexpr static std::size_t count =
(0 + ... + (is_optional<Ts>::value ? 1 : 0));
};
template<typename Tuple>
constexpr auto optional_count() -> std::size_t {
return optional_count_impl<Tuple>::count;
}
Demo
You can get the size of the tuple using std::tuple_size, then iterate over all its members using a recursive template. In that template, you can pretend to construct an instance of the tuple using std::declval, get the value at the current index using std::get, and then finally get the type of that value using decltype.
Example implementation:
#include <optional>
#include <tuple>
#include <utility>
template<typename T>
struct is_optional : std::false_type {};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template<typename Tuple, size_t i>
constexpr size_t optional_count_impl() {
size_t val = is_optional<std::remove_reference_t<decltype(std::get<i>(std::declval<Tuple>()))>>::value ? 1 : 0;
if constexpr (i) {
val += optional_count_impl<Tuple, i - 1>();
}
return val;
}
template<typename Tuple>
constexpr size_t optional_count() {
const size_t tuple_size = std::tuple_size<Tuple>::value;
if constexpr (tuple_size == 0) {
return 0;
} else {
return optional_count_impl<Tuple, tuple_size - 1>();
}
}
using Tup1 = std::tuple<int, int, std::optional<size_t>, bool, std::optional<bool>, std::optional<std::optional<int>>>;
int main() {
return optional_count<Tup1>();
}
I have class Matrix and i want a specialized method of len() if R = 3 and C = 1.
I would like to have solution with the standard library.
Inheritance is not a solution because it wont work on the base class too.
Normal template specialization like
template<class T>
class Matrix<T, 3, 1> {
public:
T len() const {return 3;}
};
would force me to implement all other Matrix<T, R, C> methodes and stuff too. (?)
Ideas:
std::enable_if
constexpr
...
#include <vector>
template<class T, std::size_t R, std::size_t C>
class Matrix {
private:
std::vector <T> data;
public:
T len() const;
};
A dirty solution would be:
template<class T, std::size_t R, std::size_t C>
T Matrix<T, R, C>::len() const {
if constexpr (R == 3 && C == 1) {
return 3;
}
throw std::runtime_error("len not available");
}
A simple solution using std::enable_if.
#include <type_traits> // std::enable_if
#include <vector> // std::vector
template <class T, std::size_t R, std::size_t C>
class Matrix {
private:
std::vector<T> data;
public:
template <std::size_t C_ = C, typename = std::enable_if_t<C_ == 1>>
std::size_t len() const {
double res = 0;
for (const auto& x : data) {
res += std::pow(x, 2);
}
return std::sqrt(res);
}
};
Alternative out-of-line definition:
template <class T, std::size_t R, std::size_t C>
class Matrix {
private:
std::vector<T> data;
public:
template <std::size_t R_ = R,
std::size_t C_ = C,
typename E1 = std::enable_if_t<R_ == 3>,
typename E2 = std::enable_if_t<C_ == 1>>
double len() const;
};
template <class T, std::size_t R, std::size_t C>
template <std::size_t R_, std::size_t C_, typename E1, typename E2>
double Matrix<T, R, C>::len() const {
double res = 0;
for (const auto& x : data) {
res += std::pow(x, 2);
}
return std::sqrt(res);
}
Example:
#include <iostream> // std::cout, std::endl
int main(int argc, char* argv[]) {
Matrix<double, 3, 1> vec;
std::cout << "vec length: " << vec.len() << std::endl;
// output: vec length: 3
// Matrix<double, 3, 3> mat;
// std::cout << "mat length: " << mat.len() << std::endl; // does not compile
return 0;
}
One possible approach:
Explanation inline as comments:
#include <iostream>
template<class T, int R, int C>
struct Matrix;
namespace MatrixOperations
{
// default handling is to prevent compilation
template<class Mat>
auto len(Mat const&) -> void = delete;
// special case for a 3,1 matrix
template<class T>
auto len(Matrix<T, 3, 1> const&) -> T
{
return T(3);
}
};
template<class T, int R, int C>
struct Matrix
{
// ForceDeduce is a defaulted type.
// The argument of this type is defaulted
// It's purpose is to ensure that this function is not actually compiled until it is used
// This will prevent the call to MatrixOperations::len producing an error unless this function
// is called
template<class ForceDeduce = int*>
T len(ForceDeduce = nullptr) const
{
return MatrixOperations::len(*this);
}
};
int main()
{
Matrix <int, 3, 1> m31;
std::cout << m31.len() << std::endl;
Matrix <int, 3, 2> m32;
// fails to compile
// std::cout << m32.len() << std::endl;
}
I have the following code where I am irritated by the fact that compiler is unable to see that variable passed as argument to a function is constexpr so I must use arity 0 function instead of 1 argument function.
I know this is not a compiler bug, but I wonder if there are idioms that enable to workaround this problem.
#include <array>
#include <iostream>
static constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
template <typename C, typename P, typename Y>
static constexpr void copy_if(const C& rng, P p, Y yi3ld) {
for (const auto& elem: rng) {
if (p(elem)){
yi3ld(elem);
}
}
}
// template<std::size_t N>
static constexpr auto get_evens(/* const std::array<int, N>& arr */) {
constexpr auto is_even = [](const int i) constexpr {return i % 2 == 0;};
constexpr int cnt = [/* &arr, */&is_even]() constexpr {
int cnt = 0;
auto increment = [&cnt] (const auto&){cnt++;};
copy_if(arr, is_even, increment);
return cnt;
}();
std::array<int, cnt> result{};
int idx = 0;
copy_if(arr, is_even, [&result, &idx](const auto& val){ result[idx++] = val;});
return result;
}
int main() {
// constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
for (const int i:get_evens(/* arr */)) {
std::cout << i << " " << std::endl;
}
}
If it is not obvious what I want: I would like to change get_evens signature so that it is template templated on array size N and that it takes 1 argument of type const std::array<int, N>&.
The error message when I change arr to be an function argument isn't helpful:
prog.cc:25:21: note: initializer of 'cnt' is not a constant expression
prog.cc:19:19: note: declared here
constexpr int cnt = [&arr, &is_even]()constexpr {
A function argument is never a constant expression, even if a function is used in constexpr context:
constexpr int foo(int i)
{
// i is not a constexpr
return i + 1;
}
constexpr auto i = 1;
constexpr auto j = foo(i);
To mimic a constexpr argument, use a template parameter:
template<int i>
constexpr int foo()
{
// i is constexpr
return i + 1;
}
constexpr auto i = 1;
constexpr auto j = foo<i>();
A possible solution is to use std::integer_sequence to encode integers into a type:
#include <array>
#include <iostream>
#include <type_traits>
template<typename P, typename Y, int... elements>
constexpr void copy_if_impl(P p, Y yi3ld, std::integer_sequence<int, elements...>) {
((p(elements) && (yi3ld(elements), true)), ...);
}
template<typename arr_t, typename P, typename Y>
constexpr void copy_if(P p, Y yi3ld) {
copy_if_impl(p, yi3ld, arr_t{});
}
template<typename arr_t>
constexpr auto get_evens(){
constexpr auto is_even = [](const int i) constexpr { return i % 2 == 0; };
constexpr int cnt = [&is_even]() constexpr {
int cnt = 0;
auto increment = [&cnt](const auto&) { cnt++; };
copy_if<arr_t>(is_even, increment);
return cnt;
}();
std::array<int, cnt> result{};
int idx = 0;
copy_if<arr_t>(is_even, [&result, &idx](const auto& val) {
result[idx++] = val; });
return result;
}
int main()
{
using arr = std::integer_sequence<int, 11, 22, 33, 44, 55>;
for (const int i : get_evens<arr>()) {
std::cout << i << " " << std::endl;
}
}
Addition suggested by Constantinos Glynos.
From Effective Modern C++ book by Scott Meyers, item 15, p.98:
constexpr functions can be used in contexts that demand compile-time constants. If the values of the arguments you pass to a constexpr function in such a context are known during compilation, the result will be computed during compilation. If any of the arguments’ values is not known during compilation, your code will be rejected.
When a constexpr function is called with one or more values that are not known during compilation, it acts like a normal function, computing its result at runtime. This means you don’t need two functions to perform the same operation, one for compile-time constants and one for all other values. The constexpr function does it all.
The other answer has a correct work around but I think the reasoning has nothing to do with parameters but instead to do with the lambda capture here:
constexpr int cnt = [/* &arr, */&is_even]()
Indeed we can test the various scenarios with this code:
#include <array>
#include <iostream>
template <size_t N>
constexpr int foo(const std::array<int, N>& arr) {
return [&arr] () { return arr.size(); }();
}
template <size_t N>
constexpr int bar(const std::array<int, N>& arr) {
int res{};
for (auto i : arr) {
res++;
}
return res;
}
template <size_t N>
constexpr int baz(const std::array<int, N>& arr) {
constexpr int test = [&arr] () constexpr {
return bar(arr);
}();
return test;
}
int main() {
constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
constexpr std::array<int, foo(arr)> test{};
constexpr std::array<int, bar(arr)> test2{};
constexpr std::array<int, baz(arr)> test3{};
}
Note that the line where test3 is initialized fails to compile. This, however, compiles just fine:
template <size_t N>
constexpr int baz(const std::array<int, N>& arr) {
return bar(arr);
}
So, what's the problem here? Well lambdas are really just glorified functors, and internally it'll look something like this:
struct constexpr_functor {
const std::array<int, 5>& arr;
constexpr constexpr_functor(const std::array<int, 5>& test)
: arr(test) { }
constexpr int operator()() const {
return bar(arr);
}
};
// ...
constexpr constexpr_functor t{arr};
constexpr std::array<int, t()> test3{};
Notice now that we get an error message showing the real problem:
test.cpp:36:33: note: reference to 'arr' is not a constant expression
test.cpp:33:34: note: declared here
constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
The other answer quotes Scotts Meyer's book but misinterprets the quotes. The book actually shows several examples of parameters being used in constexpr situations, but the quotes are simply saying that if you pass a non-constexpr parameter, the function can run at compile-time.
Following the Evg's suggestion, so passing the numbers as template parameters of a std::integer_sequence, but passing the integer sequence as argument of the get_evens() function, and not as template parameter, you can use the numbers directly inside get_evens().
I mean... you can simplify the get_evens() as follows (EDIT: further simplified following a suggestion from Evg (Thanks!))
template <typename T, T ... Ts>
constexpr auto get_evens (std::integer_sequence<T, Ts...> const &)
{
std::array<T, (std::size_t(!(Ts & T{1})) + ...)> result{};
std::size_t idx = 0;
((void)(Ts & 1 || (result[idx++] = Ts, true)), ...);
return result;
}
and you can use it this way
int main()
{
using arr = std::integer_sequence<int, 11, 22, 33, 44, 55>;
for ( const int i : get_evens(arr{}) )
std::cout << i << " " << std::endl;
}
#include <array>
#include <iostream>
static constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
template <typename C, typename P, typename T>
static constexpr void invoke_if(const C& rng, P p, T target) {
for (const auto& elem: rng) {
if (p(elem)){
target(elem);
}
}
}
constexpr bool is_even(int i) {
return i % 2 == 0;
}
template<std::size_t N>
constexpr std::size_t count_evens(const std::array<int, N>& arr)
{
std::size_t cnt = 0;
invoke_if(arr, is_even, [&cnt](auto&&){++cnt;});
return cnt;
}
template<std::size_t cnt, std::size_t N>
static constexpr auto get_evens(const std::array<int, N>& arr) {
std::array<int, cnt> result{};
int idx = 0;
invoke_if(arr, is_even, [&result, &idx](const auto& val){ result[idx++] = val;});
return result;
}
int main() {
// constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
for (const int i:get_evens<count_evens(arr)>(arr)) {
std::cout << i << " " << std::endl;
}
}
this works in g++, but in clang we get a problem because the begin on an array isn't properly constexpr with at least one library. Or maybe g++ violates the standard and clang does not.
template<auto t0, auto...ts>
struct ct_array:
std::array<decltype(t0) const, 1+sizeof...(ts)>,
std::integer_sequence<decltype(t0), t0, ts...>
{
ct_array():std::array<decltype(t0) const, 1+sizeof...(ts)>{{t0, ts...}} {};
};
template<class target, auto X>
struct push;
template<auto X>
struct push<void, X>{using type=ct_array<X>;};
template<auto...elems, auto X>
struct push<ct_array<elems...>, X>{using type=ct_array<elems...,X>;};
template<class target, auto X>
using push_t= typename push<target, X>::type;
template<class target>
struct pop;
template<auto x>
struct pop<ct_array<x>>{using type=void;};
template<auto x0, auto...xs>
struct pop<ct_array<x0, xs...>>{using type=ct_array<xs...>;};
template<class target>
using pop_t=typename pop<target>::type;
template<class lhs, class rhs, class F, class=void>
struct transcribe;
template<class lhs, class rhs, class F>
using transcribe_t = typename transcribe<lhs, rhs, F>::type;
template<auto l0, auto...lhs, class rhs, class F>
struct transcribe<ct_array<l0, lhs...>, rhs, F,
std::enable_if_t<F{}(l0) && sizeof...(lhs)>
>:
transcribe<pop_t<ct_array<l0, lhs...>>, push_t<rhs, l0>, F>
{};
template<auto l0, auto...lhs, class rhs, class F>
struct transcribe<ct_array<l0, lhs...>, rhs, F,
std::enable_if_t<!F{}(l0) && sizeof...(lhs)>
>:
transcribe<pop_t<ct_array<l0, lhs...>>, rhs, F>
{};
template<auto lhs, class rhs, class F>
struct transcribe<ct_array<lhs>, rhs, F, void>
{
using type=std::conditional_t< F{}(lhs), push_t<rhs, lhs>, rhs >;
};
template<class lhs, class F>
using filter_t = transcribe_t<lhs, void, F>;
// C++20
//auto is_even = [](auto i)->bool{ return !(i%2); };
struct is_even_t {
template<class T>
constexpr bool operator()(T i)const{ return !(i%2); }
};
constexpr is_even_t is_even{};
template<auto...is>
static constexpr auto get_evens(ct_array<is...>) {
return filter_t< ct_array<is...>, decltype(is_even) >{};
}
Live example.
Test code:
auto arr = ct_array<11, 22, 33, 44, 55>{};
for (const int i : get_evens(arr)) {
std::cout << i << " " << std::endl;
}
I'm trying to initialise a static and constant array with the cumulative product of a template parameter pack:
template <int ...D>
class Foo
{
static const std::array<const Size, sizeof...(D)> _array;
};
template <int...D> const std::array<const int, sizeof...(D)> Foo<D...>::_array =
{ cumulative_product<D...>() };
How do I write the function cumulative_product<>(), such that it transforms D... into the cumulative product of D...? E.g.
Foo<1,2,3,4>::_array;// = {1,1*2,1*2*3,1*2*3*4} = {1,2,6,24}.
Solution: Massive thank you to #bogdan for your excellent C++14 solution, and improvements to my C++11 solution.
#include <array>
#include <iostream>
#define CPP_VERSION 11
#if CPP_VERSION >= 14
// Credit: #bogdan at http://stackoverflow.com/q/37373602/6367128
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product(int seed = 1) { return{ { seed *= Args ... } }; }
#elif CPP_VERSION == 11
// Acknowledgement: Huge thank you to #bogdan for making the code more portable, concise and readable!
namespace details
{
template<int N, int i, int j, int ...Args> struct c_product_gen // N counts down to zero
{
static constexpr std::array<int, sizeof...(Args)+1> get() { return c_product_gen<N - 1, i*j, Args..., i*j>::get(); }
};
template<int i, int j, int ...Args> struct c_product_gen<0, i, j, Args...> // The end point of the template recursion
{
static constexpr std::array<int, sizeof...(Args)+1> get() { return { { j, Args... } }; }
};
}
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product() { return details::c_product_gen<sizeof...(Args), 1, Args...>::get(); }
#else // CPP_VERSION < 11
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product()
{
static_assert(false, "C++ version 11 or greater is required.");
return std::array<int, sizeof...(Args)>();
}
#endif
int main()
{
constexpr auto a = cumulative_product<1,2,3,4,5>();
for(auto i : a) std::cout << i << ' '; // Output: 1 2 6 24 120
std::cout << '\n';
}
#include <array>
#include <utility>
#include <cstddef>
template <int... D, std::size_t... Is>
constexpr std::array<int, sizeof...(D)> cumulative_product(std::index_sequence<Is...>)
{
static_assert(sizeof...(D), "Missing initializers");
int a[]{ D... };
for (int i = 1; i < int(sizeof...(D)); ++i)
{
a[i] *= a[i-1];
}
return {{ a[Is]... }};
}
template <int... D>
constexpr auto cumulative_product()
{
return cumulative_product<D...>(std::make_index_sequence<sizeof...(D)>{});
}
template <int... D>
struct Foo
{
static constexpr std::array<int, sizeof...(D)> _array = cumulative_product<D...>();
};
DEMO