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
I'm thinking about a function with signature
template<typename ...Ts>
std::vector<std::tuple<Ts...>> join_vectors(std::vector<Ts>&&...) {
//...
};
but probably a more general one accepting any iterable instead of just std::vector would be good. Probably it would have a signature like this?
template<template<typename> typename C, typename ...Ts>
C<std::tuple<Ts...>> join_vectors(C<Ts>&&...) {
// ...
};
However, I'm not at this level yet in C++ (despite doing the same in Haskell would be relatively easy), hence I seek for help.
Unfortunately, Range-v3's zip is not at my disposal in this case. I'm tagging it because I think those interested in it are in a better position to help me.
For any indexable containers with size something like this is possible:
#include <tuple>
#include <vector>
#include <algorithm>
// Copy from lvalue containers, move from rvalue containers.
template<typename ...Cs>
auto zip(Cs... vecs) {
std::vector<std::tuple<typename std::decay_t<Cs>::value_type...>> vec;
auto len = std::min({vecs.size()...});
vec.reserve(len);
for(std::size_t i=0;i<len;++i){
vec.emplace_back(std::move(vecs[i])...);
}
return vec;
};
//Return vector of tuples with & for non-const vecs and const& if const.
template<typename ...Cs>
auto zip_view(Cs&... vecs) {
std::vector<std::tuple<decltype(vecs[0])...>> vec;
auto len = std::min({vecs.size()...});
vec.reserve(len);
for(std::size_t i=0;i<len;++i){
vec.emplace_back(vecs[i]...);
}
return vec;
};
If the containers have properly implemented move constructors, this solution will copy the containers passed as lvalues and move from rvalue ones.
Very slight downside is that lvalue containers are copied whole first instead of only the individual elements.
Example [Godbolt]
#include <iostream>
#include <memory>
template<typename T, typename...Args>
void print_tuple(const T& first, const Args&... args){
std::cout<<'('<<first;
((std::cout<<','<< args),...);
std::cout<<')';
}
template<typename T>
struct helper{
using fnc_t = void;
};
template<typename...Args>
struct helper<std::tuple<Args...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename...Args>
struct helper<std::tuple<Args&...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename T>
using fnc_t2 = typename helper<T>::fnc_t;
template<typename T>
void template_apply(fnc_t2<T> f, const T& tuple){
std::apply(f, tuple);
}
template<typename T>
void print_vec(const std::vector<T>& vec){
for(const auto&e:vec){
template_apply(print_tuple,e);
std::cout<<'\n';
}
}
struct MoveOnlyFoo{
MoveOnlyFoo(int i):m_i(i){}
int m_i;
std::unique_ptr<int> ptr = nullptr;
};
std::ostream& operator<<(std::ostream& o, const MoveOnlyFoo& foo){
return o<<foo.m_i;
}
int main(){
std::vector v1{1,2,3,4,5,6};
std::vector v2{'a','b','c','d','e'};
std::vector v3{1.5,3.5,7.5};
std::vector<MoveOnlyFoo> vmove;
vmove.emplace_back(45);
vmove.emplace_back(46);
vmove.emplace_back(47);
const std::vector v4{-1,-2,-3,-4,-5};
//Move rvalues, copy lvalue.
print_vec(zip(v1,v2,v3, v4, std::move(vmove)));
// This won't work since the elements from the last vector cannot be copied.
//print_vec(zip(v1,v2,v3, v4, vmove));
std::cout<<"View:\n";
//View, provides const& for const inputs, & for non-const
print_vec(zip_view(v1,v2,v3,v4));
std::cout<<"Modify and print:\n";
for(auto& [x,y]: zip_view(v1,v2)){
++x,++y;
}
// Note the view can work with const containers, returns tuple of `const T&`.
print_vec(zip_view(std::as_const(v1),std::as_const(v2)));
}
Output
(1,a,1.5,-1,45)
(2,b,3.5,-2,46)
(3,c,7.5,-3,47)
View:
(1,a,1.5,-1)
(2,b,3.5,-2)
(3,c,7.5,-3)
Modify and print:
(2,b)
(3,c)
(4,d)
(5,e)
(6,f)
Please disregard the readability of the printing code ;)
I modeled it after python zip functionality. Note your initial proposal copies the vectors, so the output is a vector with the values moved from the parameters.
Returning an iterable Cs is harder because you would have to specify how to insert elements into it, iterators cannot do it on their own.
Getting it work with iterators (but returning still a vector) is a chore, but in theory also possible.
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);
}
Trying to override map::compare function using lambda, it seems that the following solution works.
auto cmp = [](const int&a, const int& b) { return a < b; };
std::map<int, int, decltype(cmp)> myMap(cmp);
But, I had to define cmp first and use it later.
Can I do this without defining 'cmp'?
No, you can't use lambda in unevaluated context -- i.e. template parameters as in your example.
So you must define it somewhere else (using auto) and then use decltype... the other way, as it was mentioned already is to use an "ordinal" functors
If your question is about "how to use lambda expression *once* when define a map" you can exploit implicit conversion of lambdas to std::function like this:
#include <iostream>
#include <functional>
#include <map>
int main()
{
auto m = std::map<int, int, std::function<bool(const int&, const int&)>>{
[](const int& a, const int& b)
{
return a < b;
}
};
return 0;
}
you may introduce an alias for that map type to reduce typing later...
#include <iostream>
#include <functional>
#include <map>
#include <typeinfo>
typedef std::map< int, int, std::function<bool(const int&, const int&)> > MyMap;
int main()
{
auto cmp = [](const int& a, const int& b) { return a < b; };
MyMap map(cmp);
return 0;
}
Using std::function to provide the appropriate type signature for the comparator type you can define your map type and then assign any lambda compare you wish to.
You could do something like this where the type of the map is deduced from the function you pass to a function.
#include <map>
template<class Key, class Value, class F>
std::map<Key, Value, F> make_map(const F& f) {
return std::map<Key, Value, F>{f};
}
int main() {
auto my_map = make_map<int, int>([](const int&a, const int& b) { return a < b; });
my_map[10] = 20;
}
I don't see a ton of reason for doing this but I wont say it's useless. Generally you want a known comparator so that the map can be passed around easily. With the setup above you are reduced to using template functions all the time like the following
tempalte<class F>
void do_somthing(const std::map<int, int, F>& m) {
}
This isn't necessarily bad but my instincts tell me that having a type which can ONLY be dealt with by generic functions is bad. I think it works out fine for lambda functions but that's about it. The solution here is to use std::function
#include <map>
#include <functional>
template<class Key, class Value>
using my_map_t = std::map<Key, Value, std::function<bool(const Key&, const Key&)>>;
int main() {
my_map_t<int, int> my_map{[](const int&a, const int& b) { return a < b; }};
my_map[10] = 20;
}
Now you can use any predicate you want and you have a concrete type to work with, my_map
hope this helps!
In C++20 you can do this:
std::map<int, int, decltype([](const int&a, const int& b) { return a < b; })> myMap;
int main() {
myMap.insert({7, 1});
myMap.insert({46, 2});
myMap.insert({56, 3});
for (const auto& [key,value]:myMap) {
std::cout << key << " " << value << std::endl;
}
}