In the proposed C++20 (The One) Ranges TS, what is the proposed method for converting the view into a std::vector?
The following code does not compile:
int
main() {
std::vector<float> values = {1.0, 2.0, 3.0, 4.0, 5.2, 6.0, 7.0, 8.0, 9.0};
//fmt::print("{}\n", std::experimental::ranges::views::filter(values, [] (float v) { return v < 5.f; }));
std::vector<float> foo = vw::filter(values, [] (float v) { return v < 5.f; });
fmt::print("{}\n", foo);
}
with the error
../src/view.cpp:19:40: error: conversion from ‘std::experimental::ranges::v1::filter_view<std::experimental::ranges::v1::ref_view<std::vector<float> >, main()::<lambda(float)> >’ to non-scalar type ‘std::vector<float>’ requested
std::vector<float> foo = vw::filter(values, [] (float v) { return v < 5.f; });
(the commented line will also not compile due to some CV constraints).
So how do I do anything with a view except for using a range-based for loop?
Also some bonus questions:
Is the cmcstl2 implementation I used even following the proposal? The ranges-v3 seems not to be.
Is there any documentation on the Ranges TS? The proposal PDF I found is pretty much an awfully formatted code dump in diff style. In fact directly reading the cmcstl2 sources was way easier to read for me. The cppreference seems to be lacking as well...
The C++20 method to convert a view to a std::vector (or indeed any other container) is to pass the range's begin and end members to the vector constructor that accepts 2 iterators (and an optional allocator).
I was also looking for an answer to this question. What I really wanted is an overload of the constructor of std::vector accepting a range. Approximately:
template <std::ranges::input_range R>
vector(R&& r) : vector(r.begin(), r.end()) {
}
but that isn't in C++20.
First, I implemented this:
namespace rng = std::ranges;
template <rng::range R>
constexpr auto to_vector(R&& r) {
using elem_t = std::decay_t<rng::range_value_t<R>>;
return std::vector<elem_t>{r.begin(), r.end()};
}
which works, but isn't very "rangy": https://godbolt.org/z/f2xAcd
I then did it a bit better:
namespace detail {
// Type acts as a tag to find the correct operator| overload
template <typename C>
struct to_helper {
};
// This actually does the work
template <typename Container, rng::range R>
requires std::convertible_to<rng::range_value_t<R>, typename Container::value_type>
Container operator|(R&& r, to_helper<Container>) {
return Container{r.begin(), r.end()};
}
}
// Couldn't find an concept for container, however a
// container is a range, but not a view.
template <rng::range Container>
requires (!rng::view<Container>)
auto to() {
return detail::to_helper<Container>{};
}
https://godbolt.org/z/G8cEGqeq6
No doubt one can do better for sized_ranges and containers like std::vector that have a reserve member function.
There is a proposal to add a to function to C++23 (https://wg21.link/p1206) which will do a better job than this, I'm sure.
Related
I'm working with vector classes containing either integer or floating point types. I'd like to choose one or the other function template accordingly, but the only way to deduce the type is through the subscript operator [].
Is there a way to use enable_if<is_integral< ... on the return type of the [] operator of the template parameter?
Something like:
template<class V, typename enable_if<is_integral<V::operator[]>::value, int>::type = 0>
int MyFunc(V vec)
{ return vec[0]; }
template<class V, typename enable_if<!is_integral<V::operator[]>::value, int>::type = 0>
int MyFunc(V vec)
{ return ceil(vec[0]); }
Some general suggestion
I would avoid using namespace std which your code implies (almost) that you use.
You can shorten and make more readable std::enable_if<T>::type and std::is_integral<T>::value by writing them as std::enable_if_t<T> and std::is_integral_v<T> respectively (search for "Helper types" here, for instance).
The answer to your question
You need to apply operator[] to something
What you really want to ask is if the items in the object of type V can be accessed via [], so you want to check whether something like V{}[0] is valid, so you would write something like this (as to why std::decay_t is needed, see note (¹)):
template<class V, typename std::enable_if_t<std::is_integral_v<std::decay_t<decltype(V{}[0])>>, int> = false>
But V might be not default constructible
If you want to support types V without assuming that they are default constructible you can use std::declval to "pretend" you create a value of that type (here std::decay_t is needed for the same reason as above):
template<class V, typename std::enable_if_t<std::is_integral_v<std::decay_t<decltype(std::declval<V>()[0])>>, int> = false>
And operator[] could return some proxy to the actual entities in V
In general operator[] doesn't return an object of the type which is "conceptually" the type stored in the container; for instance, for std::vector<bool>, operator[] doesn't return a bool&, but a proxy type²; in those cases, you really want to rely on the value_type type alias stored in the container:
template<class V, typename std::enable_if_t<std::is_integral_v<typename V::value_type>, int> = false>
Something might be not value_type-equipped but only provide .begin()/.end()
As a last improvement, we can also remove the assumption that the V has a value_type member alias (which is true for STL container), and simply require that it has a begin member function (STL container have it too), and make use of ranges::iter_value_t³:
template<class V, typename std::enable_if_t<std::is_integral_v<ranges::iter_value_t<decltype(std::declval<V>().begin())>>, int> = 0>
Notice that
ranges::iter_value_t<decltype(std::declval<V>().begin())>
is in general not the same same thing as
std::decay_t<decltype(*std::declval<V>().begin())>
a case of when they are different being, again, std::vector<bool>.
¹ std::vector<int>::operator[], for instance, returns int&, not int, and int& is not integral:
static_assert(std::is_integral_v<int&>); // fails
² For instance:
// this passes
static_assert(std::is_same_v<int, std::decay_t<decltype(std::declval<std::vector<int>>()[0])>>);
// this doesn't!
static_assert(std::is_same_v<bool, std::decay_t<decltype(std::declval<std::vector<bool>>()[0])>>);
// with my compiler, this one does pass
static_assert(std::is_same_v<std::_Bit_reference, std::decay_t<decltype(std::declval<std::vector<bool>>()[0])>>);
³ ranges::iter_value_t is from the header #include <range/v3/iterator/access.hpp> of the Range-v3 library. The documentation of that library is close to crap, so you can refer the doc of the analagous C++20 functionality on cppreference.
Why are you not using std::vector::value_type?
template<class V, typename enable_if<is_integral<typename V::value_type>::value, int>::type = 0>
int MyFunc(V vec)
{ return vec[0]; }
template<class V, typename enable_if<!is_integral<typename V::value_type>::value, int>::type = 0>
int MyFunc(V vec)
{ return ceil(vec[0]); }
While using std::pair I came across two different approaches to access its elements. As they both seem to be valid and working I was wondering what is the difference between them and which approach is the preferred one?
std::pair<int, int> p(1,1); // can be of any type.
int i1 = p.first; // first approach
int i2 = std::get<0>(p); // second approach
If, in a given application, either of pair or 0 is not a literal but a parameter, use get:
template<class... T> auto sum0(const T&... t) {
return (std::get<0>(t)+...);
}
template<int i> auto sqrAt(const std::pair<int,double> &p) {
const auto v=std::get<i>(p);
return v*v;
}
If both pair and 0 are present literally, using .first is plainly preferable for readability reasons (including that it indicates the conscious use of std::pair):
template<class M>
void addKeys(M &m) {
for(auto &kv : m) kv.second+=kv.first;
}
Everything about this function indicates intended use with std::map or std::unordered_map, making it very readable despite the only type named being void.
I was reading Stroustrup's blog on c++ (http://isocpp.org/blog/2014/12/myths-3) when I found an intersting piece of code:
void do_my_sort(vector<double>& v)
{
sort(v,[](double x, double y) { return x>y; }); // sort v in decreasing order
}
int main()
{
vector<double> vd;
// ... fill vd ...
do_my_sort(v);
// ...
}
Notice that the sort does not use the traditional sort(v.begin(), v.end(), ...) which Stroustrup explains:
I used a container version of sort() to avoid being explicit about the
iterators.
However, I tried the same code on my C++11 compiler but it fails to compile. I also tried the same on a C++14 compiler using ideone but it too fails to compile, saying that there is no matching call to sort.
Why is this?
Also, Stroustrup next mentions:
I could go further and use a C++14 comparison object:
sort(v,greater<>()); // sort v in decreasing order
I have used comparators like great<>() for sort in C++11 also. Why is he stating that this is a C++14 comparison object?
He wrote that himself, it is not standard. Thus you cannot find it in the standard library. You could implement it like this:
template <class Container, class Comp>
void sort (Container& cont, Comp comp) {
using std::begin;
using std::end;
std::sort(begin(cont), end(cont), comp);
}
As Clukester pointed out, there is also boost::sort that offers this functionality.
I have used comparators like great<>() for sort in C++11 also. Why is he stating that this is a C++14 comparison object?
The C++14 comparison functors have the added ability to take forwarding references for its operator() method and deduced return types. The template argument for the Function Objects collection has been changed to have a default argument of type void and using specialization for that type.
template< class T = void >
struct greater
{
constexpr bool operator()(const T &lhs, const T &rhs) const;
};
template<>
struct greater<void>
{
template< class T, class U>
constexpr auto operator()( T&& lhs, U&& rhs ) const
-> decltype(std::forward<T>(lhs) > std::forward<U>(rhs));
};
Perhaps he is using Boost's sort, not the standard sort as one would expect. So it's boost::sort, not std::sort.
Visual Studio 2013 Preview supports a C++14 feature called (according to this page) "Transparent Operator Functors". I'm not clear on what that means. The nearest C++14 proposal I found is this, but I'm not sure if it's the same thing:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421
I'm looking for a more clear explanation of what it is, why it's an improvement, and maybe a snippet demonstrating its use.
The transparent operator functors proposal is there as a way to have generalised functors that are located in <functional>. I personally believe the proposal itself has a very good example that would help illustrate the need for it. However I'll go ahead and try to explain it as well.
Suppose you have a function, a very basic function mind you:
template<typename T, typename U>
auto less_than(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)) {
return std::forward<T>(t) < std::forward<U>(u);
}
However you want to use this generalised function in the <algorithm> header. You have two options, to make it a struct functor:
struct MyLessThanFunctor {
template<typename T, typename U>
auto operator()(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)){
return std::forward<T>(t) < std::forward<U>(u);
}
};
Or in C++14, to make a polymorphic lambda:
[](auto&& t, auto&& u) -> decltype(auto) {
return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u);
}
Both are very verbose when used in an algorithm like so:
int main() {
std::vector<int> v = {112,12,1281271,1919101,29181,412,1 };
std::sort(std::begin(v), std::end(v), MyLessThanFunctor()); // one
std::sort(std::begin(v), std::end(v), [](auto&& t, auto&& u) -> decltype(auto) {
return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u);
});
}
This proposal aims to make it more compact and generalised by doing this instead:
std::sort(std::begin(v), std::end(v), std::less<>());
This gives you perfect forwarding and solves issues with truncation or problems that arise from changing the container but not the underlying type appointed by the container as mentioned by the paper.
Suppose you have a non-generalised functor:
struct Functor {
bool operator()(uint32_t a, uint32_t b) {
return a < b;
}
};
And you use it with your std::vector<uint32_t> and it works all fine, but you forget about your functor not being generalised and use it with your std::vector<uint64_t>. Can you see the issue that has arisen? The elements will be truncated before being compared which is probably not what the user wanted. Generalised functors solve this issue for you before they arise.
Using visual studio 2008 with the tr1 service pack and Intel C++ Compiler 11.1.071 [IA-32], this is related to my other question
I'm attempting to write a functional map for c++ which would work somewhat like the ruby version
strings = [2,4].map { |e| e.to_s }
So i've defined the following function in the VlcFunctional namespace
template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
vector<U> transformedValues(container.size());
int index = -1;
BOOST_FOREACH(const auto& element, container)
{
transformedValues.at(++index) = f(element);
}
return transformedValues;
}
and you can call this like so (Note that the function template arguments are defined explicitly):
vector<int> test;
test.push_back(2); test.push_back(4);
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
return ToString(i);
});
Or like so (Note that the function template arguments aren't defined explicitly)
std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);
But crucially, NOT LIKE THIS
vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });
Without the explicit definition of hte template arguments, it doesn't know which template to use and falls over with a compile error
..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)
Having to define the template arguments makes it a much more bulky syntax and I'm aiming for minimal cruft at the call site - any ideas on why it doesn't know how do the conversion? Is this a compiler issue or does the language not allow for this type of template argument inference?
The problem is that a lambda is not a std::function even if it can be converted. When deducing type arguments, the compiler is not allowed to perform conversions on the actual provided arguments. I would look for a way to have the compiler detect the type U and let the second argument free for the compiler to deduce:
template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...
Now the issue is what to write in XXX. I don't have the same compiler that you do, and all C++0x features are still a little tricky. I would first try to use decltype:
template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...
Or maybe type traits if the compiler does not support decltype yet.
Also note that the code you are writting is quite unidiomatic in C++. Usually when manipulating containers the functions are implemented in terms of iterators, and your whole map is basically the old std::transform:
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );
Where std::transform is the C++ version of your map function. While the syntax is more cumbersome, the advantage is that you can apply it to any container, and produce the output to any other container, so the transformed container is not fixed to std::vector.
EDIT:
A third approach, probably easier to implement with your current compiler support is manually providing just the return type of the lambda as template argument, and letting the compiler deduce the rest:
template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
std::vector<LambdaReturn> ret;
std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
return ret;
}
int main() {
std::vector<int> v{ 1, 2, 3, 4, 5 };
auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}
Even if you want to add syntactic sugar to your map function, there is no need to manually implement it when you can use existing functionality.