Related
I need a template function that checks if the first value is among those that follow.
I thought I would try something like this but it doesn't work
template<class T, class U>
bool is_in_set(const T &t, const U &u) {
return t == u;
}
template<class T, class ...Args>
bool is_in_set(const T &t, Args...args) {
return false || is_in_set(t, args...);
}
It compiles but I got the following warning warning C4717 : 'is_in_set' : recursive on all control paths, function will cause runtime stack overflow
Can anyone help me to fix it and explain me why it doesn't work?
From C++17, you can write this function with a fold-expression, which is simpler than writing a recursive function with a base case.
template<class T, class ...Args>
bool is_in_set(T const & t, Args const & ...args)
{
return (... || (t == args));
}
And now you can call it like this.
is_in_set(1, 2, 3, 4); // false
is_in_set(1, 2, 1, 4); // true
Here's a demo
Considering your code, you're getting the warning because you have an infinite recursion on this line:
return false || is_in_set(t, args...); // infinite recursion here
Note that you're calling the function template recursively with exactly the same arguments. This means you'll recurse infinitely, and never reach the base case.
You can fix this by naming the second argument and taking the remaining arguments as a parameter pack.
template<class T, class U>
bool is_in_set(T const & t, U const &u)
{
return t == u;
}
template<class T, class U, class ...Args>
bool is_in_set(T const & t, U const & u, Args const &...args)
{
return is_in_set(t, u) || is_in_set(t, args...);
}
Here's a demo
I have implemented a simple fold function in C++ that accepts a lambda, and can fold multiple vectors at the same time at compile time. I am wondering if it could be simplified in some manner (I have provided both a recursive version and an iteratively recursive version - I am unsure which should have better performance): https://godbolt.org/z/39pW81
Performance optimizations are also welcome - in that regard is any of the two approaches faster?
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperR(Function&& func, const type_identity& id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I>0)
{
return func(foldHelperR<I-1>(std::forward<Function>(func), id, head, tail...), head[I], tail[I]...);
}
else
{
return func(id, head[0], tail[0]...);
}
}
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperI(Function&& func, const type_identity id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I<N-1)
{
return foldHelperI<I+1>(std::forward<Function>(func), func(id, head[I], tail[I]...), head, tail...);
}
else
{
return func(id, head[N-1], tail[N-1]...);
}
}
template<typename type_identity, typename type_head, int N_head, typename ...type_tail, int ...N_tail, typename Function = void (const type_identity&, const type_head&, const type_tail&...)>
constexpr auto fold(Function&& func, const type_identity& id, const tvecn<type_head, N_head>& head, const tvecn<type_tail, N_tail>&... tail)
{
static_assert(std::is_invocable_v<Function, const type_identity&, const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments (possibly wrong argument count).");
static_assert(all_equal_v<N_head, N_tail...>, "Vector sizes must match.");
//return foldHelperR<N_head-1>(std::forward<Function>(func), id, head, tail...);
return foldHelperI<0>(std::forward<Function>(func), id, head, tail...);
}
int main()
{
tvecn<int,3> a(1,2,3);
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
}
and can fold multiple vectors at the same time at compile time
Not exactly: if you want to operate compile-time
(1) you have to define constexpr the tvecn constructor and
(2) you have to define constexpr the foldhelper function and
(3) you have to declare constexpr a
// VVVVVVVVV
constexpr tvecn<int,3> a(1,2,3);
(4) you have to place the result of fold in a constexpr variable (or, more generally speaking, in a place where the value is required compile time, as the size field of a C-style array, or a template value parameter, or a static_assert() test)
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, a, a);
I am wondering if it could be simplified in some manner
Sure.
First of all: if you can, avoid to reinventing the weel: your tvecn is a simplified version of std::array.
Suggestion: use std::array (if you can obviously)
Second: you tagged C++17 so you can use folding
Suggestion: use it also for all_equal
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
More in general: when you have to define a custom type traits that has to provide a number, inherit (if possible) from std::integral_constant (or std::bool_constant, or std::true_type, or std::false_type: all std::integral_constant specializations). So you automatically inherit all std::integral_constant facilities.
Third: almost all C++ standard uses std::size_t, not int, for sizes.
Suggestion: when you have to do with sizes, use std::size_t, not int. This way you can avoid a lot of annoying troubles.
Fourth: from main() you should return only EXIT_SUCCESS (usually zero) or EXIT_FAILURE (usually 1)
Suggestion: avoid things as
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
Fifth: never underestimate the power of the comma operator.
Suggestion: avoid recursion at all and use template folding also for the helper function; by example
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
that you can call as follows from fold()
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
The following is a full compiling, and simplified, example
#include <array>
#include <utility>
#include <iostream>
#include <type_traits>
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
template <typename type_identity, typename type_head, std::size_t N_head,
typename ...type_tail, std::size_t ...N_tail,
typename Function = void (type_identity const &,
type_head const &,
type_tail const & ...)>
constexpr auto fold (Function && func, type_identity const & id,
std::array<type_head, N_head> const & head,
std::array<type_tail, N_tail> const & ... tail)
{
static_assert( std::is_invocable_v<Function, const type_identity&,
const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments"
" (possibly wrong argument count).");
static_assert( all_equal_v<N_head, N_tail...>,
"Vector sizes must match.");
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
}
int main()
{
constexpr std::array<int, 3u> b{2, 5, 7};
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, b, b);
std::cout << f << std::endl;
}
With Fold expression, it might be:
template <typename F, typename Init, std::size_t... Is, typename... Arrays>
constexpr auto fold_impl(F&& f, Init init, std::index_sequence<Is...>, Arrays&&... arrays)
{
auto l = [&](Init init, std::size_t i){ return f(init, arrays[i]...); };
return ((init = l(init, Is)), ...);
}
template <typename F, typename Init, typename Array, typename ... Arrays>
constexpr auto fold(F&& f, Init init, Array&& array, Arrays&&... arrays)
{
static_assert(((arrays.size() == array.size()) && ...));
return fold_impl(f, init, std::make_index_sequence<array.size()>{}, array, arrays...);
}
Demo
I am experimenting with C++ recursive templates and I do not know why my template is not working.
Say I want to define a recursive function that takes a variable number of arguments (for different types).
I've have looked at many examples of variadic templates, and all that I've seen so far use a separate template specialisation to specify the base case.
However, I think it would be nicer (in some cases at least) to use a single template, that defines the base case as well as the recursive cases.
I think this approach is especially nice if you have a lot of common logic in the function, which you would have to duplicate for your base case instance (exact same code in two different places).
The second template in the example below is supposed to be my solution. I would think that this template should be functioning on it's own. However this is not the case.
Without the first template, the code does not compile:
error: no matching function for call to
'add_elems'
return head[i] + add_elems(i, second, tail...);
^~~~~~~~~
in instantiation of function
template specialization 'add_elems<double, std::__1::vector<double, std::__1::allocator<double> >>' requested here
...
Apparently the template braks when tail consists of just one parameter. But shouldn't add_elems(i, second, tail...) then still be valid for the template
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail) with an empty tail?
I do not know if this is compiler dependent, but I am using clang.
#include <iostream>
#include <vector>
/* This template is the exact same as the below template with an
empty parameter pack as tail. I want my code to be working
without this specialisation */
template<typename V, typename S>
V add_elems(size_t i, const std::vector<V>& head, const S& second)
{
/* Imagine some more code here */
return head[i] + second[i];
}
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
/* Imagine some more code here (the same as above) */
if (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...);
else
return head[i] + second[i];
}
int main()
{
std::vector<double> a({1, -3, -3});
std::vector<double> b({2, -2, 1});
std::vector<double> c({4, -4, -11});
std::vector<double> d({4, 10, 0});
std::cout << "Result: " << add_elems(0, a, b, c, d);
std::cout << " ," << add_elems(1, a, b, c, d);
std::cout << " ," << add_elems(2, a, b, c, d);
}
The problem is that your if statement is not constexpr. Meaning that all code paths need to be compilable for every potential call to add_elems
This means that eventually you end up at a case where tail is just one element, and the compiler needs to evaluate add_elems(size_t&, const, std::vector<double>&), which doesn't exist because there's no second argument.
If you were able to have a constexpr if statement, then this would all work nicely because the compiler wouldn't even compile the bad branch when it evaluates to false, and therefore wouldn't look for a nonexistent function:
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...);
else
return head[i] + second[i];
}
Demo (requires Clang 3.9.1 or greater and -std=c++1z option.)
For what it's worth, if you have access to C++17, you can achieve this with a unary right fold:
template<typename... T>
decltype(auto) add_elems(size_t i, const T&... elems)
{
return (elems[i] + ...);
}
Demo 2 (requires Clang 3.6.0 or greater and -std=c++1z option.)
Waiting for C++17, I propose a C++11 not-so-nice solution, following the AndyG one
template <typename T0, typename ... T>
auto add_elems2 (size_t i, T0 const & elem0, T const & ... elems)
-> decltype(elem0[i])
{
using unused=int[];
auto ret = elem0[i];
unused a { (ret += elems[i], 0)... };
return ret;
}
As the error message says, the problem you have at the moment is that the call add_elems(i, second, tail...) doesn't match the definition of the function, in the case where tail is empty. Even though the boolean expression in the if statement is constexpr, until c++1z the whole body of the function has to be valid.
#AndyG provides one way that c++1z can deal with this issue, another is with if constexpr, which allows a "compile time branch". Either of those allow you to have one (primary) specialisation of your template.
// Only in c++1z
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
/* Imagine some more code here (the same as above) */
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...); // this is not required to be valid when the if is false
else
return head[i] + second[i]; // this is not required to be valid when the if is true (but it is happens to be valid anyway)
}
You can use Boost.Hana to emulate the behaviour of if constexpr in C++14. For example:
template <typename...>
struct is_empty_pack : hana::integral_constant<bool, false> {};
template <>
struct is_empty_pack<> : hana::integral_constant<bool, true> {};
template <typename T, typename... Ts>
auto sum(T const& t, Ts const&... ts) {
return hana::if_(is_empty_pack<Ts...>{},
[](auto const& t) { return t; },
[](auto const& t, auto const&... ts) { return t + sum(ts...); }
)(t, ts...);
}
As many have noted, this is easy in C++1z. It can be done in C++14, it is just hard.
template<class True, class False>
True pick( std::true_type, True t, False ) {
return std::move(t);
}
template<class True, class False>
False pick( std::false_type, True, False f ) {
return std::move(f);
}
template<bool b>
constexpr std::integral_constant<bool, b> bool_k;
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
return
pick( bool_k<(sizeof...(tail)>0)>,
[&](const auto&... tail)->V{
// tail... template argument hides function argument above:
return head[i] + add_elems(i, second, tail...);
},
[&]()->V{
return head[i] + second[i];
}
)
( tail... );
};
we do a compile time dispatch using pick to one of two lambdas.
These lambdas take the part of the code that varies by auto parameter, which makes them templates. So long as they are valid for some set of auto parameters (even ones they are "never called with"), they are legal C++.
What we have readly done is hide the two overloads within the lambdas. As C++11 doesn't have template lambdas, this "hidden overload" technique won't work in C++11.
I would like to write function as this find:
multi_set<int, string, double, myType> m; //vector of tuples
m.insert(/*some data*/);
m.find<1,2>("something",2.123);
Or
m.find<0,3>(1,instanceOfMyType);
m.find<1>("somethingelse");
Where find can be parametrized corresponding to any subset of tuple parameters.
My code so far:
template <typename ... T>
class multi_set{
typedef tuple < T... > Tuple;
vector<tuple<T...>> data = vector<tuple<T...>>();
public:
void insert(T... t){
data.push_back(tuple<T...>(t...));
}
template<size_t ... Pos>
void find(???){
// then I would like to use those params to search through data and
// return first matching item
}
}
// test whether a particular tuple is a match
template<size_t... Pos>
static bool is_match(const Tuple& tuple, const typename std::tuple_element<Pos, Tuple>::type &... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == args)... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos>
typename vector<Tuple>::const_iterator find(const typename std::tuple_element<Pos, Tuple>::type &... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, args...); });
}
It's also possible to have find take a type parameter pack and perfectly forward, rather than taking fixed types with tuple_element. The benefit is that you can avoid an unnecessary conversion if == is transparent. The cost is that you can't take anything that can't be perfectly forwarded any more (e.g., braced initializer lists, 0 as a null pointer constant). A side benefit appears to be that MSVC 2013 doesn't choke on this version:
// test whether a particular tuple is a match
template<size_t... Pos, class... Args>
static bool is_match(const Tuple& tuple, Args&&... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == std::forward<Args>(args))... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos, class... Args>
typename vector<Tuple>::const_iterator find(Args&&... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, std::forward<Args>(args)...); });
}
You should look into boost::multi_index. It is very close to what you are looking for.
http://www.boost.org/doc/libs/1_54_0/libs/multi_index/doc/tutorial/index.html
This is a function that takes a seed value, and a set of lambdas. It feeds that seed value through each of the lambdas in turn:
template<class... Fs, class R>
R chain( R r, Fs&&... fs ) {
using in_order = int[];
(void)(in_order{0,
(
(r = std::forward<Fs>(fs)( r ))
, void(), 0
)...
});
return r;
}
Inside your class, we use the above:
template<size_t... Pos, class...Us>
typename std::vector<Tuple>::const_iterator
find(Us const&... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && (std::get<Pos>(tup) == us);
}...
);
}
);
}
this compiles in clang, but not g++ 4.9.2 -- g++ doesn't like parameter packs inside lambdas.
Note the fact we take Us const&... -- this allows for transparent ==, which is important in some cases. std::string == char const* is a classic example, where if you force find to take the same value as in the tuple, you'll force a needless allocation in calling find.
In C++1z, the chain call can be replaced with:
( ... && (std::get<Pos>(tup) == us) )
which is conceptually identical, but much easier to read. This is known as a "fold expression".
Now, a problem with the above is that it uses forwarding references, which causes imperfect forwarding problems of perfect forwarding.
The most annoying of which is the inability to use {} to construct arguments.
If we use matching types, we instead force non-transparent comparison, which can be expensive (examine std::string compared to "hello this is a c string" -- it causes possibly allocation if we force the c string into a std::string.)
A way around this is to type erase down to the concept of equality with a given type.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>( *static_cast<std::decay_t<U>*>(const_cast<void*>(p)) );
return F{}( u, std::forward<Ts>(ts)... );
}),
ptr( static_cast<void const*>(std::addressof(u)) )
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{} >>
erase_view_op(U&& u):erase_view_op( std::forward<U>(u), 0 ){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op( block_deduction<U>&& u ):erase_view_op( std::move(u), 0 ){}
erase_view_op( erase_view_op const& ) = default;
erase_view_op( erase_view_op&& ) = default;
R operator()( Ts... ts ) const {
return f( ptr, std::forward<Ts>(ts)... );
}
};
struct equality {
template<class lhs, class rhs>
bool operator()(lhs const& l, rhs const& r)const {
return l==r;
}
};
template<class T>
using erase_equal_to = erase_view_op< equality, bool(T const&), T >;
using string_equal_to = erase_equal_to< std::string >;
int main() {
static_assert( std::is_same< bool, std::result_of_t< std::equal_to<>(decltype("hello"), std::string const&) > >{}, "hmm" );
string_equal_to s = "hello";
string_equal_to s2 = {{"hello"}};
(void)s2;
std::string x = "hello";
std::string y = "jello";
std::cout << s(x) << s(y) << '\n';
}
then we rewrite find:
template<size_t... Pos>
typename std::vector<Tuple>::const_iterator
find(erase_equal_to< std::remove_reference_t<std::tuple_element_t<Pos, Tuple>> >... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && us(std::get<Pos>(tup));
}...
);
}
);
}
which does both transparent equality and allows {} based construction (well, it does require {{}} based construction -- the outer to say we are constructing the eraser, the inner to construct the T).
Writing a general minimum function, Two questions came to my mind. The code works fine with any input type and different argument number:
namespace xyz
{
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b) -> decltype(a+b)
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, Args ... args) -> decltype(a+b)
{
return min(min(a, b), args...);
}
}
int main()
{
cout << xyz::min(4, 5.8f, 3, 1.8, 3, 1.1, 9) << endl;
// ^ ^ ^
// | | |
// float double int
}
Is there a better replacement for decltype(a+b)? I thing there's a standard class which I can't remember, something like decltype(std::THE_RESULT<a,b>::type).
The returned type of that decltype(std::THE_RESULT<a,b>::type)is const & or not ?
std::common_type(c++11):
For non-specialized std::common_type, the rules for determining the
common type between every pair T1, T2 are exactly the rules for
determining the return type of the ternary conditional operator where
T1 and T2 are the types of its second and the third operands.
and
For arithmetic types, the common type may also be viewed as the type
of the (possibly mixed-mode) arithmetic expression such as T0() + T1()
+ ... + Tn().
Not sure about const&, but you could play with std::remove_cv and std::remove_reference (and std::is_reference to find out the answer).
In fact, here's a list of type support utilities. Knock yourself out.
After the answer and worth comments I did it as below:
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b)
-> typename std::common_type<const T1&, const T2&>::type
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, const Args& ... args)
-> typename std::common_type<const T1&, const T2&, const Args& ...>::type
{
return min(min(a, b), args...);
}