Greetings.
I don't know very well how to explain myself, but I believe a piece of code will make you understand what I'm intenting to do :
template<class A, class B>
void myFunction(A<B>& list)
{
typename A<B>::iterator current = list.begin();
typename A<B>::iterator end = list.end();
while (current != end)
{
current++;
}
}
Where A is an STL container (vector, list...). It's like inception, but with templates : a template, inside a template, etc...
The thing is : what do you do when one of the params of your template is itself a template... and still want to support every types supported by this template.
This of course doesn't compile (it says 'A is not a template').
Does someone knows how to create such a template ?
You are looking for a template template parameter
template<template<class T, class All = std::allocator<T> > class A, class B>
void myFunction(A<B>& list)
{
typename A<B>::iterator current = list.begin();
typename A<B>::iterator end = list.end();
while (current != end)
{
current++;
}
}
However, in your particular case, I think you'd be better off by just passing the intantiated container, that is,
template<class C>
void myFunction(C& list)
{
...
}
use like this
vector<char> v;
myFunction(v);
Your original code would have to be called like this:
myFunction<std::vector, char> (v)
which is much more verbose and has no particular benefit
A and B will be concrete types (and not templates), thus A<B> makes no sense.
You can write your code this way:
template<class List>
void myFunction(List &list)
{
typename List::iterator current = list.begin();
typename List::iterator end = list.end();
while (current != end)
{
current++;
}
}
If you need to know what is the type of an element of that list, there is a typedef inside of the list for that:
typename List::value_type
Well, to solve this little example problem. It's quite simple. vector<int> is a class, so, you don't need to declare the A<B> in the prototype. You can just do this:
template<class A>
void myFunction(A& list)
{
typedef typename A::value_type B; //do this if you need to know the type of the elements.
typename A::iterator current = list.begin();
typename A::iterator end = list.end();
while (current != end)
{
current++;
}
}
But if you really need to, you can also declare the template argument as a template too:
template< template<class> class A, class B >
void myFunction(A<B>& list)
{
typename A<B>::iterator current = list.begin();
typename A<B>::iterator end = list.end();
while (current != end)
{
current++;
}
}
But the above is not really recommended and most class templates have a set of nested typedefs (like iterator and value_type in STL containers) so that you can access have all the information you need about the type without having to use these template template parameters. So, the first way is usually the preferred and more normal way to do it (it also is usually less trouble to make it work, i.e., the compiler tends to "dislike" template template parameters).
Additionally, you cannot use STL containers very easily with template template parameters because STL containers all have these "hidden" template arguments (e.g. "allocator", and "compare" for sorted containers). So, you would have to list all these as well, otherwise the compiler will not be able to make the match. And then, you won't have a very "generic" function because you will have to assume so much about the STL container that is past that it will only serve one or two types of containers. It is really better to use the first way.
Related
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 want to create a template class which has an iterator of a STL container as a member. That is how far I got:
#include <iostream>
#include <vector>
using namespace std;
template<typename Element, template <class> class StdLibContainer>
struct ClassHoldingAnIteratorToAStandardContainer
{
ClassHoldingAnIteratorToAStandardContainer(){}
typename StdLibContainer<Element*>::iterator std_lib_iterator;
};
int main()
{
vector<int> vec{1,2,3};
ClassHoldingAnIteratorToAStandardContainer<int,vector<int>> holding_iterator_to_vec;
//DOES NOT WORK, compiler says: expected a class template, got ‘std::vector<int>’
return 0;
}
Could you explain the syntax template <typename> class StdLibContainer?
I found it somewhere on stackoverflow. BUt I don't understand it.
How can I create an instance of ClassHoldingAnIteratorToAStandardContainer ? All my attempts failed so far. The compiler always gives the error message: `expected a class template, got ‘std::vector’
In the above example i want to assign holding_iterator_to_vec vec.begin().
template <typename> class is the same as template <class> class. Originally, when templates were introduced, they allowed two equivalent forms:
template<class T> struct Foo {};
// or
template<typename T> struct Foo {};
Do not ask me why! However, the same was not true for template template parameters:
template <template <class> typename T> struct Foo {};
was the only allowed syntax. Apparently, people were unhappy about it, so the syntax was relaxed.
As for your second question, std::vector takes at least two template arguments, data type and allocator. This is why a single argument template doesn't cut it before C++17. After C++17, it would work.
To make it universal, use
template<template <class...> class Container> struct Foo{};
Unless you really need to know the type of the container, I would strongly recommend to keep your ClassHoldingAnIteratorToAStandardContainer independent of the concrete container type. If you just need the iterator, this is simpler and sufficient:
template<typename iterator>
struct iterator_wrapper {
iterator iter;
};
Thats the minimum you need to have an iterator as member :).
I dont really know what you want to use the iterator for, so just for the sake of an example lets add methods that actually use the iterator....
#include <iterator>
#include <vector>
#include <iostream>
template<typename iterator>
struct iterator_wrapper {
using value_type = typename std::iterator_traits<iterator>::value_type;
iterator iter;
bool operator!=(const iterator& other) { return iter != other;}
iterator_wrapper& operator++(){
++iter;
return *this;
}
const value_type& operator*() { return *iter; }
};
template <typename iterator>
iterator_wrapper<iterator> wrap_iterator(iterator it) {
return {it};
}
int main() {
std::vector<int> vec{1,2,3};
auto it = wrap_iterator(vec.begin());
for (;it != vec.end();++it) std::cout << *it;
}
Also there is a problem in your code.
typename StdLibContainer<Element*>::iterator
is for containers of pointers while in main you have ints. If you want to infer the iterator type from the container type then you can do it for example like this:
template <typename container,
typename iterator = typename container::iterator>
iterator_wrapper<iterator> wrap_begin(container& c) {
return {c.begin()};
}
which makes creating an iterator_wrapper as simple as
auto x = wrap_begin(vec);
Note that this answer applies to C++11, in newer standards there are deduction guides that make such make_x methods more or less superfluous afaik.
The title is not the best so let me explain: I'm experimenting with a custom mini-stl (for learning purposes) and currently implementing the std::distance function. The function must behave differently for random access iterators and simple input iterators.
Function overloading
Both std (in Microsoft's implementation) and EASTL use operator overloading to select the appropriate function, something like this:
namespace internal
{
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType DistanceImpl(Iter first, Iter last, InputIteratorTag)
{
// ...
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType DistanceImpl(Iter first, Iter last, RandomAccessIteratorTag)
{
// ...
}
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType Distance(Iter first, Iter last)
{
// the last parameter of the function selects the proper DistanceImpl
return internal::DistanceImpl<Iter>(first, last, (typename IteratorTraits<Iter>::IteratorCategory)());
}
I guess the temporary object (the category tag parameter which is an empty struct) will be optimized away since it's not used.
Class specialization with static function
What's about the specialization of a helper class with a static function?
namespace internal
{
template <typename Cat>
struct DistanceImpl;
template <>
struct DistanceImpl<InputIteratorTag>
{
template <typename Iter>
inline static typename IteratorTraits<Iter>::DifferenceType Calc(Iter first, Iter last)
{
// ...
}
};
template <>
struct DistanceImpl<RandomAccessIteratorTag>
{
template <typename Iter>
inline static typename IteratorTraits<Iter>::DifferenceType Calc(Iter first, Iter last)
{
// ...
}
};
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType Distance(Iter first, Iter last)
{
return internal::DistanceImpl<typename IteratorTraits<Iter>::IteratorCategory>::Calc<Iter>(first, last);
}
Questions
Difference(s) between the two solution? (Including performance and reliability)
Advantages/Disadvantages?
Tag dispatch automatically handles inheritance hierarchies; explicit specializations don't. This is particularly important with iterators, because the standard iterator category tags form an inheritance hierarchy: random_access_iterator_tag derives from bidirectional_iterator_tag which derives from forward_iterator_tag which derives from input_iterator_tag.
The first version works out of the box when given a forward or bidirectional iterator by selecting the input iterator overload. The second doesn't and requires additional specializations or some other changes.
I have a class named A, and in this class I have an iterable container, which I do iterate through following some rules of access -- order, emptiness, and others.
To simplify the following example, lets consider I'm just iterating through the container, but this cannot be done using the built-in container's iterator.
class A {
public:
class iterator {
public:
// Constructor
iterator() {
}
// Destructor
~iterator() {
}
// Advances the iterator
void operator++() {
// Some accessing policy
}
};
private:
std::vector<int> a;
};
Everything works tremendously fine -- and looks very neat --, except that, when I do declare my iterator, I must use typename -- which I pretty much assume that is used in order to tell the compiler that what I have is a type, and not a class instanciation itself.
Questions:
why do I have to use typename when I do:
A a;
for (typename A::iterator it(...); it != ...; ++it) {
}
How are iterators commonly defined, since a vector iterator does not require the typename tag? Does it have to do with declaring the vector from the class definition, instead of from the vector itself?
std::vector<int> v;
for (std::vector<int>::iterator it(v.begin()); it != v.end(); ++it) {
}
Are the iterators defined inside the container class -- I guess it's named composition --, or, if not, how is it iterators are added to the namespace of a class, like in:
std::vector<int>::iterator it;
1 - why do I have to use typename when I do: [...]
You do not have to use typename. The typename disambiguator is required inside a template when you are using a dependent, qualified type name. This Q&A on StackOverflow clarifies things. This:
A a;
typename a::iterator it; // ERROR!
Is illegal C++. Provided that A is not the name of a template parameter, you should just do:
A::iterator it;
If you are inside a template and A is the name of a template parameter, such as in:
template<typename A>
struct X
{
void foo()
{
typename A::iterator it;
// ^^^^^^^^
// This is necessary here!
}
};
Then you have to use typename to tell the compiler that what follows the :: is the name of a type.
2 - How are iterators commonly defined, since a vector iterator does not require the typename tag?
Again, it is not true that "a vector iterator does not require the typename tag". If you have an explicit specialization of that vector, such as in:
std::vector<int>::iterator it; // "typename" not required
Then typename is not required, as it wasn't required in A::iterator it. If you are inside a template as in the following case, however, it will be required:
template<typename A>
struct X
{
void foo()
{
typename std::vector<A>::iterator it;
// ^^^^^^^^
// This is necessary here!
}
};
That's because std::vector<A>::iterator here is a qualified, dependent type name.
3 - Are the iterators defined inside the container class -- I guess it's named composition --, or, if not, how is it iterators are added to the namespace of a class, like in [..]
This could be done by defining a nested class, or simply done by using type aliases:
template<typename T>
struct X
{
typedef T* iterator;
iterator begin() { /* ... */ }
// ...
};
X<int>::iterator it; // OK: "typename" not required
template<typename T>
void foo(X<T>& x)
{
typename X<T>::iterator it = x.begin();
// ^^^^^^^^
// This is necessary here!
// ...
}
There are a lot of problems with your example code, so this may be the answer you are looking for (waves hand) :) Of course if your examples are not correct, then all bets are off.
I'm surprised that this would work at all. 'a' is a variable, 'A' is the class.
Also, when declaring a variable with a default constructor, you do not use ending parenthesis ().
A a;
A::iterator it;
for (A::iterator it; it != ...; ++it) {
}
Also, iterators are defined within the container class. Using typename should only be necessary when you are dealing with templates and only when the accessing something that could be interpreted as either a static member or function/nested class or typedef. This can be further explained by the answer here which was also given by Andy Prowl.
Good luck
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.