I have a vector. I want to compare it with an aggregate to see if they are equal. For example
vector<int> v{0, 1, 2};
bool b = equal(v, {0, 1, 2}); // how to write a method to implement it???
Just compare them by == operator.
bool v_equal(const std::vector<int>& v1, const std::vector<int>&v2) {
return v1==v2;
}
Call the function like:
bool isEqual = v_equal(v1,{0, 1, 2});
You can use an std::initializer_list for this purpose, eg
#include <iostream>
#include <vector>
#include <algorithm>
bool equal(const std::vector<int>& v1, const std::initializer_list<int>& v2) {
return std::equal(v1.begin(), v1.end(), v2.begin(), v2.end());
}
int main() {
std::vector<int> data1 = {1,2,3,4};
std::cout << std::boolalpha << equal(data1, {1,2,3,4}) << std::endl;
return 0;
}
You can even use template template arguments to write a generic function which is able to evaluate two collections as long as they provide iterators and operator== for contained types, something like:
template<typename T, template<typename...> class C1, typename U, template<typename...> class C2>
bool is_equal(const C1<T>& v1, const C2<U>& v2) {
return std::equal(v1.begin(), v1.end(), v2.begin(), v2.end());
}
But to make deduction work with emphasized textstd::initializer_list you need to cast them appropriately then, like (std::initializer_list<int>){1,2,3,4}.
The std::equal overload which accepts 4 iterators is C++14 only though, if you don't have access to it you must check that length is the same.
Related
I would like to create a const std::vector to contain all the elements of two other const std::vector of the same type. Since the vector is const I can not concatenate it step by step with the two const std::vector using the method mentioned in Concatenating two std::vectors.
#include <iostream>
#include <vector>
int main()
{
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints;
for (int i: all_ints)
std::cout << i << ' ';
return 0;
}
For the example above I would like to define all_ints in a way that the output is 0 1 2 3.
How could that be done?
Make a function that takes the other two vectors, creates a third one, inserts values from the first two, returns the result by value. Then assign this to your const vector:
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints = concat(int_a, int_b);
I actually don't know what's the essence of creation of const vectors like this. But a simple hack is to create a temporary non-const vector and fill it with the first two vectors, then create the final const vector. eg:
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
std::vector<int> middle(int_a);
middle.insert(middle.begin(),int_b.begin(),int_b.end());
const std::vector<int> all_ints(middle);
As suggested in comments, the last line could be written as:
const std::vector<int> all_ints = std::move(middle);
As already mentioned in #Ayxan Haqverdili's answer, you can create a concatenation function that will be used to initialize your vector.
I propose the following implementation for such a function:
template <template <typename, typename> typename C, typename ... Args>
C<Args...> concat(const C<Args...> & lhs, const C<Args...> & rhs)
{
C<Args...> res(lhs.cbegin(), lhs.cend());
res.insert(res.cend(), rhs.cbegin(), rhs.cend());
return res;
}
Note: This implementation is generalized to all standard library sequence containers except std::array.
Which can then be used like this:
const std::vector<int> a {1, 2, 3};
const std::vector<int> b {4, 5};
const std::vector<int> ab = concat(a, b);
Live example here
An alternative and simpler version could be:
template <typename C>
C concat(const C & lhs, const C & rhs)
{
C res(lhs.size() + rhs.size());
typename C::iterator it = std::copy(lhs.cbegin(), lhs.cend(), res.begin());
std::copy(rhs.cbegin(), rhs.cend(), it);
return res;
}
Live example here
Here's yet another implementation that can also easily be modified to even modify the contents of all_ints at a future time. It does not require recent c++ versions.
#include <vector>
#include <algorithm>
int main()
{
const std::vector<int> int_a{ 0,1 };
const std::vector<int> int_b{ 2,3 };
const std::vector<int> all_ints(int_a.size()+ int_b.size());
std::vector<int>& all_intsr = const_cast<std::vector<int>&>(all_ints);
std::copy(int_b.begin(), int_b.end(), std::copy(int_a.begin(), int_a.end(), all_intsr.begin()));
}
This takes advantage of a being able to legally cast a const vector& to a vector with the following restriction to prevent UB. One may not modify the vector object. This does not include the contents owned by the vector which is not const. Neither begin() or end() modify the vector object. Also modifying element of it later are legal such as this.
all_intsr[3]=42;
If you are just looking to iterate over both vectors, you can do it using a custom view concatenator. The following is much more efficient than creating (and destroying) another container just to iterate over both ranges.
#include <iostream>
#include <array>
#include <ranges>
#include <vector>
#include <utility>
template<typename T1, typename T2>
auto concat_view(T1& lhs, T2& rhs)
{
static_assert(std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>);
using T1_ = std::remove_reference_t<T1>;
using T2_ = std::remove_reference_t<T2>;
if constexpr (std::is_const_v<T1_> || std::is_const_v<T2_>)
{
using Iter = typename std::decay_t<T1>::const_iterator;
return std::array<std::ranges::subrange<Iter>, 2>{std::as_const(lhs), std::as_const(rhs)} | std::views::join;
}
else
{
using Iter = typename std::decay_t<T1>::iterator;
return std::array<std::ranges::subrange<Iter>, 2>{lhs, rhs} | std::views::join;
}
}
int main()
{
std::vector<int> v1{1,2,3}, v2{4,5,6};
for (int& val : concat_view(v1, v2))
++val;
for (const auto& val : concat_view(std::as_const(v1), v2))
std::cout << val << '\n';
return 0;
}
i want to initialize a const std::vector<int> member variable in the initializer list of a constructor, given a std::vector<std::tuple<int, float>> constructor argument. The vector should contain all the first tuple items.
Is there a one-liner that extracts an std::vector<int> containing all the first tuple entries from an std::vector<std::tuple<int, float>> ?
With C++20 ranges:
struct X
{
const std::vector<int> v;
template <std::ranges::range R>
requires std::convertible_to<std::ranges::range_value_t<R>, int>
X(R r)
: v{r.begin(), r.end()}
{}
X(const std::vector<std::tuple<int, float>>& vt)
: X{vt | std::ranges::views::elements<0>}
{}
};
With ranges-v3:
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | ranges::views::transform([] (auto t) {
return std::get<0>(t); })
| ranges::to<std::vector>()}
{}
};
And a Frankenstein's monster:
#include <ranges>
#include <range/v3/range/conversion.hpp>
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | std::ranges::views::elements<0>
| ranges::to<std::vector>()}
{}
};
Not a one-liner to setup, but certainly one to use - you can write a function to do the conversion, and then the constructor can call that function when initializing the vector member, eg:
std::vector<int> convertVec(const std::vector<std::tuple<int, float>> &arg)
{
std::vector<int> vec;
vec.reserve(arg.size());
std::transform(arg.begin(), arg.end(), std::back_inserter(vec),
[](const std::tuple<int, float> &t){ return std::get<int>(t); }
);
return vec;
}
struct Test
{
const std::vector<int> vec;
Test(const std::vector<std::tuple<int, float>> &arg)
: vec(convertVec(arg))
{
}
};
Adding to #bolov's answer, let's talk about what you might have liked to do, but can't in a one-liner.
There's the to<>() function from ranges-v3 from #bolov 's answer - it materializes a range into an actual container (a range is lazily-evaluated, and when you create it you don't actually iterate over the elements). There is no reason it shouldn't be in the standard library, if you ask me - maybe they'll add it 2023?
You may be wondering - why do I need that to()? Can't I just initialize a vector by a range? And the answer is - sort of, sometimes, maybe. Consider this program:
#include <vector>
#include <tuple>
#include <ranges>
void foo()
{
using pair_type = std::tuple<int, double>;
std::vector<pair_type> tvec {
{12, 3.4}, {56, 7.8}, { 90, 91.2}
};
auto tv = std::ranges::transform_view(
tvec,
[](const pair_type& p) { return std::get<0>(p);}
);
std::vector<int> vec1 { tv.begin(), tv.end() };
std::vector<std::tuple<int>> vec2 {
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.begin(),
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.end()
};
}
The vec1 statement will compile just fine, but the vec2 statement will fail (GodBolt.org). This is surprising (to me anyway)!
You may also be wondering why you even need to go through ranges at all. Why isn't there a...
template <typename Container, typename UnaryOperation>
auto transform(const Container& container, UnaryOperation op);
which constructs the transformed container as its return value? That would allow you to write:
std::vector<int> vec3 {
transform(
tvec,
[](const pair_type& p) { return std::get<0>(p); }
)
}
... and Bob's your uncle. Well, we just don't have functions in the C++ standard library which take containers. It's either iterator pairs, which is the "classic" pre-C++20 standard library, or ranges.
Now, the way I've declared the transform() function, it is actually tricky/impossible to implement generally, since you don't have a way of converting the type of a container to the same type of container but with a different element. So, instead, let's write something a little easier:
template <
typename T,
template <typename> class Container,
typename UnaryOperation
>
auto transform(const Container<T>& container, UnaryOperation op)
{
auto tv = std::ranges::transform_view(container, op);
using op_result = decltype(op(*container.begin()));
return Container<op_result> { tv.begin(), tv.end() };
}
and this works (GodBolt.org).
Yes, there is a one-liner if you use this library in Github.
The code goes like this.
#include <iostream>
#include <tuple>
#include <vector>
#include <LazyExpression/LazyExpression.h>
using std::vector;
using std::tuple;
using std::ref;
using std::cout;
using LazyExpression::Expression;
int main()
{
// Define test data
vector<tuple<int, float>> vecTuple { {1,1.1}, {2,2.2}, {3, 3.3} };
// Initialize int vector with the int part of the tuple (the one-liner :)
vector<int> vecInt = Expression([](const tuple<int, float>& t) { return std::get<int>(t); }, ref(vecTuple))();
// print the results
for (int i : vecInt)
cout << i << ' ';
cout << "\n";
}
// Output: 1 2 3
For example
template<class Container, class List>
bool isEqual(Container const& c, List const& l)
{
return c == Container(l); // Error!!
}
And check by
std::vector<int> v;
bool b = isEqual(v, {1, 2, 3});
But error in my code. No conversion from list to container. How to fix the bug?
Your example, as currently written, will not only fail to compile because of the comparison, but also because the template parameter List cannot be deduced from a braced-init-list.
Either change the function to
template<class Container, class T>
bool isEqual(Container const& c, std::initializer_list<T> const& l)
or change the way you call it
std::vector<int> v;
auto l = {1, 2, 3};
bool b = isEqual(v, l);
// or
bool b = isEqual(v, std::initializer_list<int>{1, 2, 3});
To fix the comparison, as Igor mentions in the comments, use
return c.size() == l.size() && std::equal(std::begin(c), std::end(c), std::begin(l));
Or, if you have access to the C++14 overload of std::equal that takes begin and end iterators to both ranges, you can skip the size check
return std::equal(std::begin(c), std::end(c), std::begin(l), std::end(l));
Python allows you to write if e in arr: ... and if key in dict: ... which is handy.
Can we do something similar with the latter using std::find() and std::map ? That will allow me to uniformly handle std::array and std::map with a single generic function, without explicitly switching to std::map::find().
But if overloading operator==() is the only way, I'd rather give up this idea...
Update: note I already have a sort of solution
By "overloading operator==()" I meant something like this:
template<typename K>
struct KF {
K&& k;
template <typename V>
friend bool operator==(const typename std::pair<const K, V>& pair, const KF<K>& o) {
return pair.first == o.k;
}
};
template <typename K>
KF<K> keyFinder(K&& k) { return KF<K>{ std::forward<K>(k) }; }
int main() {
std::set<int> s{ 1, 2, };
cout << (std::find(s.begin(), s.end(), 1) == s.end()) << endl; // => 0
cout << (std::find(s.begin(), s.end(), 3) == s.end()) << endl; // => 1
std::map<int, int> m{ {1,10}, {2,20}, };
cout << (std::find(m.begin(), m.end(), keyFinder(1)) == m.end()) << endl; // => 0
cout << (std::find(m.begin(), m.end(), keyFinder(3)) == m.end()) << endl; // => 1
}
http://ideone.com/7ULUe9
Things get more complicated when we deal with non-scalar K in an universal way (perfect forwarding etc. ?)
...why not write your own utility function?
template <typename TContainer, typename TValue>
bool contains(const TContainer& c, const TValue& x);
You can use overloading to match the containers:
template <typename TValue, std::size_t N>
bool contains(const std::array<TValue, N>& c, const TValue& x)
{
return std::find(std::begin(c), std::end(c), x) != std::end(c);
}
template <typename TValue, typename... Ts>
bool contains(const std::map<Ts...>& c, const TValue& x)
{
return c.find(x) != std::end(c);
}
Usage:
std::array<int, 2> a{1,2};
std::map<int, int> b{{1,2},{3,4}};
assert(contains(a, 1));
assert(!contains(a, 42));
assert(contains(b, 1));
assert(!contains(b, 42));
live example on wandbox
If you want to support additional containers in the future, it's a good idea to use SFINAE to check whether or not a particular expression is valid. This approach works well because it doesn't care about the type of the container, it only cares about what operations can be performed on it.
The detection idiom would likely make it very easy to check member availability through SFINAE (and its implementation is C++11 compatible).
I also wrote an article about checking expression validity in-situ with C++17, which could be an interesting read. Despite its title, it covers C++11, C++14 and C++17 techniques to check expression validity:
"checking expression validity in-place with C++17"
To answer your explicit question - no, std::find won't be able to work uniformly for std::map/std::unordered_map and std::array/std::vector as the former is a collection of key/value pairs and the latter is collection of values...
You may want to use std::find_if instead as it gives you a little bit more flexibility on defining equality condition e.g. like this (c++1z approach):
#include <array>
#include <map>
#include <string>
#include <algorithm>
#include <type_traits>
template <class T>
struct is_pair: std::false_type { };
template <class K, class V>
struct is_pair<std::pair<K,V>>: std::true_type { };
int main() {
std::map<std::string, int> m {{"abc", 1}, {"cde", 2}, {"efg", 3}};
std::array<int, 5> a{1, 2, 3, 4, 5};
auto lambda = [](auto it) {
if constexpr (is_pair<decltype(it)>::value) {
return it.second == 3;
} else {
return it == 3;
}
};
assert(std::find_if(a.begin(), a.end(), lambda) != a.end());
assert(std::find_if(m.begin(), m.end(), lambda) != m.end());
}
[live demo]
Have in mind that this approach won't work as expected if you decide to search through collection like std::vector<std::pair<int, int>>.
In C++03 the following code works fine:
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::vector<int> v2;
v2.push_back(2);
v2.push_back(3);
v2.push_back(4);
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), std::max<int>);
return 0;
}
In C++11 this doesn't work because it added an overload for std::max that contains an initializer_list. Therefore, you have to use a very ugly cast to choose the correct overload:
static_cast<const int& (*)(const int&, const int&)>(std::max)
I have a few questions.
Why did the standard committee decide to do this knowing it would (probably) break existing code and force the user to create an ugly cast?
Are future standards of C++ going to attempt to alleviate this problem?
What is a workaround?
If you are doing this sufficiently frequently, you might want to write a transparent functor wrapper:
struct my_max {
template<class T>
const T& operator()(const T& a, const T& b) const{
return std::max(a, b);
}
};
Then you can simply do
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), my_max());
whenever you need it, rather than writing a lambda or a cast each time. This is basically the same idea as the transparent operator functors - let the template arguments be deduced at the actual call site rather than explicitly specified when you create the functor.
If you want to make this fancier, you can even have operator() take heterogeneous types and add perfect forwarding and use trailing return types:
struct my_max {
template<class T, class U>
constexpr auto operator()( T&& t, U&& u ) const
-> decltype(t < u ? std::forward<U>(u) : std::forward<T>(t)){
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
In C++14, this is simplified to
struct my_max {
template<class T, class U>
constexpr decltype(auto) operator()( T&& t, U&& u ) const{
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
What is a workaround?
A lambda is probably the most readable and useful for predicates and comparators:
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(),
[] (int a, int b) {return std::max(a,b);} );
You might want to check out T.C.s functor if you need it more often. Or, with C++14:
auto max = [] (auto&& a, auto&& b) -> decltype(auto)
{return a > b? std::forward<decltype(a)>(a) : std::forward<decltype(b)>(b);};
Why did the standard committee decide to do this knowing it would
(probably) break existing code and force the user to create an ugly
cast?
The only explanation is that they found the new overload to bring enough joy to compensate the breaking of existing code and the need for workarounds in future.
You could just use std::max_element instead of this new overload, so you trade the syntax sugar for passing std::max-specializations as predicates for the syntax sugar of finding the maximum element within a couple of variables without explicitly creating an array for it.
Basically
std::transform( ..., std::max<int> );
// <=>
std::transform( ..., [] (int a, int b) {return std::max(a,b);} );
vs
int arr[] {a,b,c,d}; // You don't have an array with a,b,c,d included consecutively yet
int maximum = *std::max_element( std::begin(arr), std::end(arr) ); // ensure arr non-empty!
// <=>
auto maximum = std::max({a, b, c, d});
Maybe it does compensate? On the other hand, you barely ever need the latter.
Are future standards of C++ going to attempt to alleviate this
problem?
I don't think so. Apparently, the standard committee really doesn't like to remove recently introduced features. I don't really see that much of a problem either; The lambda does the job.
Although I've accepted T.C's answer which provides a comprehensive breakdown, as stated in a comment, I want to mimic the transparent comparator functor for class templates like std::less. This answer is provided for critique by others incase there's anything wrong with the syntax.
template <typename T = void>
struct my_max;
template <>
struct my_max<void> {
template<class T, class U>
constexpr decltype(auto) operator()( T&& t, U&& u ) const {
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
If you are ok with using C++20 ranges additions you can just use std::ranges::max.
std::ranges::max is not a function, but a struct so it can be passed to the algorithms.
Example:
#include <iostream>
#include <algorithm>
#include <vector>
#include <fmt/ranges.h>
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::vector<int> v2;
v2.push_back(2);
v2.push_back(3);
v2.push_back(4);
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), std::ranges::max);
std::cout << fmt::format("{}", v2);
}
If you have recent boost you can use BOOST_HOF_LIFT macro.
fmt lib in example is just for printing vector, all you need is boost
#include <type_traits>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/hof/lift.hpp>
#include <fmt/ranges.h>
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::vector<int> v2;
v2.push_back(2);
v2.push_back(3);
v2.push_back(4);
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), BOOST_HOF_LIFT(std::max<int>));
std::cout << fmt::format("{}", v2);
}