I like STL algorithms, and prefer use algorithms rather than usual loops.
Almost all STL algorithms are usually used as:
std::algorithm_name( container.begin(), container.end(), ..... )
container.begin(), container.end() - is one of most popular words pair in my projects.
Does anybody have the same problem?
How do you Guys solve this problem?
What could you propose to avoid this duplication? I see a few ways for solution, but all of them have different limitations (macro usage, not compatible with usual pointers, etc.).
The next C++ standard, C++0X (where X stands for, hopefully, 9) will add the possibility to change from iterator perspective to container perspective. You will be able to do eg.
std::sort(my_vec);
If you cant wait for this I would recommend you to look at: Boost.Range
And if you are a really interested in iterators/ranges I would recommend you to read Andrei's "iterators must go"
Many have encountered this nuisance. Though the iterator concept is exceptionally general, it lacks some usability.
Enter the 'range' concept. It is to be preferred to avoid any code duplication. So if you encounter .begin() and .end() pairs all over the code, it's good practice to create a layer in between the 'iterator-getting' and the actual algorithms.
References:
RangeLib
CodeProject
...
#define ALL(x) (x).begin(), (x).end()
sort(ALL(vec));
First, I don't think it's a big issue. In general, I don't really care about typing a few more characters. readability is more important, and I think begin/end is perfectly readable.
Shorter container names can help though (con.begin() is easier to type than container.begin())
And passing iterators around instead of the container itself means that you don't have to call begin/end more than once in any case.
It's not really something that bothers me.
If it got really bad, I would probably create a bunch of templates for my most commonly used algorithms in a new namespace:
namespace my_ranged_algorithms {
// Metafunction for extracting an appropriate iterator from
// the container type
template <typename T>
struct get_iterator_type_for;
// For vectors
template <typename T>
struct get_iterator_type_for<std::vector<T> > {
typedef typename std::vector<T>::iterator type;
};
template <typename T>
struct get_iterator_type_for<std::vector<T> const> {
typedef typename std::vector<T>::const_iterator type;
};
// For C arrays
template <typename T, size_t N>
struct get_iterator_type_for<T(&)[N]> {
typedef T* type;
};
// Generic begin() and end() wrappers
// For all standard containers
template <typename Cont>
typename get_iterator_type_for<Cont>::type begin(Cont& c) {
return c.begin();
}
template <typename Cont>
typename get_iterator_type_for<Cont>::type end(Cont& c) {
return c.end();
}
// For C arrays
template <typename T, size_t N>
typename get_iterator_type_for<T (&)[N]>::type begin(T (&c)[N]) {
return c;
}
template <typename T, size_t N>
typename get_iterator_type_for<T (&)[N]>::type end(T (&c)[N]) {
return c + N;
}
// Finally, the actual algorithm wrappers
// copy
template <typename Cont, typename OutIter>
OutIter copy(Cont& from, OutIter to) {
return std::copy(begin(from), end(from), to);
}
// remove
template <typename Cont, typename T>
typename get_iterator_type_for<Cont>::type remove(Cont& from, T x) {
return std::remove(begin(from), end(from), x);
}
// etc.
};
Then call them like so:
vector<int> a, b;
using namespace my_ranged_algorithms;
copy(a, back_inserter(b));
b.erase(remove(b, 42), b.end()); // Remember to call erase() after remove()!
This nice presentation [PDF] about a possible future solution to this was recently linked from reddit. it discusses how to fully replace iterators with the range concept.
C++11 has addressed this minor annoyance within the language.
boost::range_ex will solve this before c++0x.
And it's not hard to write a few wrappers yourself in the meantime.
Related
In Stroustrup's "A Tour of C++" there is a code snippet
template<typename C>
using Value_type = typename C::value_type; // the type of C’s elements
template<typename Container>
void algo(Container& c)
{
/* (1) */
Vector<Value_type<Container>> vec; // keep results here
// ...
}
Why we need this using, how it differs from writing in (1) just
Vector<Container::value_type> vec;
The reason is this declaration:
Vector<Container::value_type> vec;
is not actually valid, and is an error. Instead you need to write:
Vector<typename Container::value_type> vec;
which is more verbose.
The purpose of the alias template Value_type is to make it more convenient to use a member type alias of Container without having to say typename each time.
I am trying to create a concept ElementIterable which can determine the type is nested ranges or not. For example, the elements in std::vector<int> is not iterable, but the elements (std::vector<int>) in std::vector<std::vector<int>> is iterable. The idea about using std::iterator_traits<T> comes up in my mind and the experimental code is as following. However, this ElementIterable concept doesn't work as the expected behavior. Is there any idea to fix this ElementIterable concept?
template<typename T>
concept ElementIterable = requires(typename std::iterator_traits<T>::value_type x) // requires-expression
{
x.begin(); // must have `x.begin()`
x.end(); // and `x.end()`
};
The usage of this ElementIterable is here.
template<typename T> requires ElementIterable<T>
void Foo(T input);
template<typename T> requires ElementIterable<T>
void Foo(T input)
{
std::cout << "Element iterable" << std::endl;
}
template<typename T>
void Foo(T input);
template<typename T>
void Foo(T input)
{
std::cout << "Element not iterable" << std::endl;
}
The usage of the function Foo.
int number = 1;
std::vector<decltype(number)> vector1;
vector1.push_back(number);
Foo(vector1); // Element not iterable
std::vector<decltype(vector1)> vector2;
vector2.push_back(vector1);
Foo(vector2); // Element not iterable
// but expected behaviour is: Element iterable
All suggestions are welcome.
If you want to ask if a type is a range which itself contains a range, that's simply applying the std::range type twice:
template<typename T>
concept nested_range = std::ranges::range<T> && std::ranges::range<std::ranges::range_value_t<T>>
range_value_t extracts the value_type from the iterator type of the range. Here's a live example.
Well, the problem is std::iterator_traits<T>. The argument for iterator_traits is supposed to be an iterator type. Meanwhile, you want the concept to be applicable unto containers. Since std::iterator_traits is designed to be SFINAE friendly, and it's unlikely a container will satisfy enough of the legacy iterator concept, it's more than likely std::iterator_traits<T> has no members when you check your concept. That leads to the concept not being satisfied.
Why not rely on the concepts in the ranges header? It has a handy utility to obtain the value type of a type that satisfies the range concept
#include <ranges>
template<typename T>
concept ElementIterable = requires(std::ranges::range_value_t<T> x)
{
x.begin(); // must have `x.begin()`
x.end(); // and `x.end()`
};
Or, slightly more robust and without reinventing standard traits
template<typename T>
concept ElementIterable = std::ranges::range<std::ranges::range_value_t<T>>;
C++20 concepts can be as dumb (as in, text replacing) as you need them to be. In your case, simple template duck typing should do the job by checking for what you need to exist, ie iterator functions in the results of your type's iterator functions.
With that in mind, you can try something like this:
template<typename T>
concept ElementIterable = requires(T x)
{
x.begin()->begin();
x.end()->end();
};
I'm writing different sort functions, which take two iterators and sort sequence. I would like to implement them for any kind of vector and make them typesafe, like this:
template <typename T>
void itsort(std::vector<T>::iterator begin, std::vector<T>::iterator end)
{
// code
}
But due to errors I'm only able to implement something type-unsafe:
template <typename T>
void itsort(T begin, T end)
{
// code
}
How to implement type-safe template function, which takes two vector iterators?
PS: Currently there is no need in comparator, all sorts work with different types of numbers.
Determining if something is an iterator of a vector is hard, and mostly pointless.
The type of vector iterators are extremely free under the standard. They can even be naked pointers in some implementations.
What more, the type of the vector that produces the iterator might not be deducible from the iterator. As an example, a std::vector<int, some_allocator>::iterator might be a different, or the same type, as a std::vector<int>::iterator. They could both be int*, or they could be some class wrapped around a pointer. They could be the same type, or different types. (The technique of using the same type for different container types is called SCARY iterators if you want to find discussion about it).
In general, you cannot determine if a given iterator is a vector iterator.
You can write code that will fail if the given iterator is not a random-access iterator, and deduce the type T. First, a bit of boilerplate to make the access to iterator_traits a bit less verbose:
namespace iterators {
template<class It>
using iterator_category =
typename std::iterator_traits<It>::iterator_category;
template<class It>
using value_type =
typename std::iterator_traits<It>::value_type;
}
now we do this:
template<class It>
void itsort(It begin, It end)
{
using T = iterator::value_type<It>;
using category = iterator::iterator_category<It>;
static_assert(
std::is_base_of<std::random_access_iterator_tag, category>::value, "This function only supports random-access iterators"
);
}
this "fails late", in that the error does not occur at the time of overload resolution (SFINAE), but it does check your types for you.
You also have access to the underlying value type of your iterators.
Doing this in a SFINAE friendly way is difficult, because iterator_traits is not mandated to be SFINAE friendly, and iterator_traits is the mandated way to determine the traits of an iterator. As a concrete example, void* often matches the empty iterator_traits for pointers, but then it fails because it isn't an iterator.
seems you are looking for something like this:
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
template< typename T >
void itsort(T, T, std::bidirectional_iterator_tag)
{
std::cout << "itsort called for bidirectional iterator\n";
}
template <typename T>
void itsort(T, T, std::random_access_iterator_tag)
{
std::cout << "itsort called for random-access iterator\n";
}
template< typename T >
void alg(T first, T last)
{
itsort(first, last, typename std::iterator_traits<T>::iterator_category());
}
int main()
{
std::vector<int> v;
alg(v.begin(), v.end());
std::list<int> l;
alg(l.begin(), l.end());
}
This questions begs a more preparation, so I provide some bits of code first and then the exact question
Assuming I have the following type declared
template<typename T>
struct some_type
{
T t_;
};
which would be constructed with a factory function like so
typedef float numeric_type;
std::vector<std::string> construction_material;
//Push_back of strings in certain form...
std::vector<std::unique_ptr<some_type<numeric_type>> instances;
build_instances(construction_material.begin(), construction_material.end(), back_inserter(instances));
and the construction function would be something like following
template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
for(input_iterator iter = begin; iter != end; ++iter)
{
//This won't work, but to illustrate some ideas...
//build_instance<std::iterator_traits<output_iterator>::value_type>(*iter)
}
//[...]
return *out;
}
template<typename T>
std::unique_ptr<some_type<T>> build_instance(std::string const& material)
{
static_assert(std::is_floating_point<T>::value == true, "The template type needs to be a floating point type.");
std::unique_ptr<some_instance<T>> instance(new some_instance<T>());
//Some processing...
return instance;
}
I know I could change the function to return some container (or perhaps even templatize the container type), like
template<typename input_iterator, typename T>
std::vector<std::unique_type<T>> build_instances(input_iterator begin, input_iterator end,
output_iterator out)
{
//Likewise code to the previous snippets...
return ...
}
The problems I haven't been able to solve are:
Would it be possible -- or impossible -- to use the back_inserter like approach? It looks like being the most flexible for the caller?
How to get a hold on the numeric_type in build_instances body (as in having it through output_iterator) so that it can be used in building the instances one-by-one?
How to ensure the caller knows to wait for the objects wrapped in std::unique_ptrs? An alternative would be just as plain pointers, but I'm not enthuasiastic about that.
There's a similar kind of question with a heading How can I make this template method more elegant? (or: less explicit template parameters required), which takes a container and transforms it to a different type of a container.
Edit
As commented to Jogojapan's comment that currently I transform the input like so
std::transform(construction_material.begin(), construction_material.end(), std::inserter(instances, instances.begin()), build_instance<numeric_type>);
but the subsequent function calls need to be supplied the numeric_type typedef too, which is somewhat cumbersome. I hope to avoid that. It looks I was mistaken, but for the purpose of education and all, would it be possible to further reduce the need to typedef the numeric type and deduce it from the iterator?
An intrusive solution is to let some_type exposes its type parameter, the same way std::unique_ptr<T, D> exposes its first parameter via element_type (which we will need later):
template<typename T>
struct some_type
{
// give it an appropriate meaningful name
using value_type = T;
value_type t_;
};
template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
using pointer_type = typename std::iterator_traits<output_iterator>::value_type;
using value_type = typename pointer_type::element_type::value_type;
return std::transform(begin, end, out, build_instance<value_type>);
}
You can also non-intrusively extract the first template parameter of a template specialization:
template<typename T>
struct first;
template<template<typename...> class Template, typename First, typename... Pack>
struct first<Template<First, Pack...>>> {
using type = First;
};
template<typename T>
using First = typename first<T>::type;
The value_type alias in build_instances would instead become
using value_type = First<typename pointer_type::element_type>;
As a final remark I find it a bit odd that build_instance take a T parameter but constructs instances of some_type<T>. If it took T and constructed instances of T (where perhaps T is restricted to be a specialization of some_type.) This would have spared your problem, too.
This code does not automatically infer the return type correctly (a design aspect of C++):
template < typename Container,
typename UnaryOp>
Container
mymap(Container c, UnaryOp op)
{
typedef typename Container::value_type ResultType
Container<ResultType> result;
for(Container::iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
What I would like to do is have something like this happen:
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic;
foomatic = mymap(bar, [] (string s)->int {return atoi(s.c_str());});
//foomatic now is equal to {1,2,3}
I was figuring that Container would be inferred to be vector, and ResultType would be inferred to be int.
After the question changed:
You are using the same type, Container, for input and output. But your input and output types are distinct: your input is vector<string>, whereas your output is vector<int>. No wonder that C++ refuses to compile this.
Your problem is now to deduce the return type from the input types. Generally, C++ cannot do that. It’s as simple as that: overload resolution and template resolution only happens based on the input arguments, never on the return type (in some cases elaborate tricks involving proxy objects and implicit casts can be used to work around this but let’s not go there).
The simplest and most idiomatic solution is just to specify the return element type manually when calling the function, as in:
foomatic = mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
This requires that the return element type be put first in the template argument list:
template <
typename ResultType,
template<typename> class Container,
typename InputType,
typename UnaryOp>
Container<ResultType> mymap(Container<InputType> c, UnaryOp op) { ... }
However, that does not work because std::vector does not fit the declaration of template<typename> class. Why? Simple reason: because it has more than just one template argument. In particular, the standard says that it has at least one extra template argument to specify the allocator.
Solution: declare the template argument as template<typename, typename> class, right?
No. Now, this does work for some standard library implementations. But besides the mandated two template arguments, the containers may have additional template arguments that take default values (this is often used to pass policy classes to a container, for example; the allocator is already such a policy class).
This is a fundamental problem: we cannot declare Container so that it conforms to all possible type signatures of containers in C++. So this solution, too, is a no-go.
The best solution is unfortunately more complicated, we need to rebind the container type explicitly. This we can do via an extra metafunction:
template <typename C, typename T>
struct rebind;
We need to partially specialize this metafunction for each possible number of template parameters. For example, to make it work with the minimal std::vector, we’d need the following partial specialization:
template <
template <typename, typename> class C,
typename Old,
typename New,
typename A>
struct rebind<C<Old, A>, New> {
typedef typename A::template rebind<New> Rebound;
typedef C<New, typename Rebound::other> type;
};
This looks daunting. What it does is take an existing std::vector<foo> and a type bar and rewrite it to a std::vector<bar>. The tricky part is that we also need to rewrite the allocator type. This is done via the rather complicated Rebound declaration.
Now we can write your function, and invoke it:
template <
typename ResultType,
typename C,
typename UnaryOp>
typename rebind<C, ResultType>::type
mymap(C const& c, UnaryOp op)
{
typename rebind<C, ResultType>::type result;
for(typename C::const_iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
int main() {
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic =
mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
}
Piece of cake. A really, really, complicated cake.
Answer to old question:
If you have a template parameter that is itself a class template, you need to declare it as such:
template <
template<typename> class Container,
typename ResultType,
typename UnaryOp>
Container<ResultType> mymap(Container<ResultType> c, UnaryOp op) { ... }
The template<typename> class Container mimics the class template declaration syntax and tells the compiler that “Container is a class template that expects a single template argument.”
But libraries usually avoid these nested template declarations and instead rely on traits/metafunctions to communicate such information. That is, it would usually be written as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Container::value_type ResultType;
}
(The typename in the typedef is necessary because the name is a dependent name and C++ cannot figure out that this it names a type.)
This example mimics the standard library convention of having a typedef value_type inside each container for its associated value type. Other libraries may follow different schemas. For example, I am contributing to a library that uses external metafunctions that work as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Value<Container>::Type ResultType;
}
The idea is the same, the only difference is that Container::value_type has been “outsourced” to an independent type.
You need something along the lines of:
template<typename Container, typename UnaryOp>
auto mymap(Container c, UnaryOp op) -> Container::rebind<decltype(op(*c.begin()))>
{
typedef typename Container::value_type InputType;
typedef decltype( op( InputType() ) ) ResultType;
typedef typename Container::rebind<ResultType> ResultContainer;
// ...
}
You can use a trick known as auto_cast, which we will rewrite a little bit to be specific to containers.
template<typename container> struct auto_cast_container {
container c;
template<typename out_type> operator out_type() {
return out_type(c.begin(), c.end());
}
};
template<typename Container, typename UnaryOperator>
auto
mymap(const Container& c, UnaryOperator op)
-> auto_cast_container<std::vector<decltype(op(*c.begin()))>> {
std::vector<decltype(op(*c.begin()))> retval;
std::for_each(c.begin(), c.end(), [&](decltype(*c.begin())& ref) {
retval.push_back(op(ref));
});
auto_cast_container<std::vector<decltype(op(*c.begin()))>> return_value;
return_value.c = std::move(retval);
return return_value;
}
Effectively, the templated conversion operator allows for conversion to any type which will accept the begin/end constructor. This means that you can map from a vector to a list, if you like, and it can also map pairs into associative containers and back again, should you need it. If you're hunting for efficiency this can be tuned further but I left that out for clarity.
Edit: Konrad's comment pointed out a couple of logical flaws. I also improved the safety and transparency of the system by using decltype in all appropriate cases.