I want to determine if something is a container. Right now I'm testing it on containers that are formatted to have a type and allocator. My ultimate goal is to learn how to write a container template that, when containing layers of itself can allow direct iteration on its innermost elements. E.g. if there was a container of 3 containers each containing 3 more containers, each containing 3 elements, I want to be able to iterate through all 27 elements in one range based for loop. Distinguishing whether something is a container that directly contains elements or contains containers of elements (or containers of containers...of containers of elements) using SFINAE to check whether the element has an iterator seems like a logical first step towards determining whether the begin() function should return the iterator to the element or the result of said element's begin() function. If I can get my first step to work, I'd like to include that logic into writing my container, but I can't get it to compile:
#include <vector>
#include <iostream>
template <typename T,
template <typename E, typename Allocator = std::allocator<E>> class Container
>
void is_cont(typename Container<T>::iterator it)
{
std::cout << "is iterator\n";
}
template <typename T,
template <typename E, typename Allocator = std::allocator<E>> class Container
>
void is_cont(Container<T>& cont)
{
std::cout << "is container\n";
}
int main()
{
std::vector<int> vec{ 2, 4, 6 };
is_cont(vec); // Output: "is container"
//is_cont(vec.begin()); // COMPILER ERROR
}
How can I fix this?
Your definition of whether something is or is not a container appears to be based solely on whether it has an iterator inner class.
That's as good of a heuristic as any, I suppose, but let's go with that.
It most instances, SFINAE is much simpler if it's done with classes, instead of trying to implement SFINAE with a function, like your is_container(). Making your is_container() into a class results in a following, textbook SFINAE solution:
#include <vector>
#include <string>
#include <iostream>
#include <type_traits>
template<typename ...>
using void_t=void;
template<typename T, class=void> struct is_container : std::false_type {};
template<typename T>
struct is_container<T, void_t<typename T::iterator>> : std::true_type {};
int main()
{
std::cout << is_container<int>::value << std::endl;
std::cout << is_container<std::vector<int>>::value << std::endl;
return 0;
}
Now, a related question would be whether there's a better way to check if something is a container, or not. Whichever heuristic you choose, it's trivial to adjust a class-based test, instead of a function-based. For example, if you want to consider whether something is a container if it has both iterator and const_iterator inner classes, just need to change one line:
template<typename T>
struct is_container<T, void_t<typename T::iterator,
typename T::const_iterator>> : std::true_type {};
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.
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());
}
Is there a way in C++ to specify the type of the inner template in a nested template? What I want to do is
template<template <std::string> class StringContainer>
void function(const StringContainer& cont);
In other words, I want a function that accepts all types of string containers -- vectors, deques, lists, etc.
Basically, I have four questions:
Is it possible to do so?
If so, how should the definition look like (the above doesn't compile, but I didn't expect it to)?
Even if possible, is there a way to make it work with the standard containers, which have more than one template parameters, which might even be implementation-specific?
If not containers, can I at least expect iterators to have only one template parameter?
Thanks!
#include <deque>
#include <list>
#include <iostream>
#include <vector>
// Removed:
// template < template<typename, typename ...> class Container,
// typename T, typename ...Types>
// void function(const Container<T, Types...>&);
template < template<typename ...> class Container, typename ...Types>
void function(const Container<std::string, Types...>& container) {
for(const std::string& s: container) {
std::cout << s << " ";
}
std::cout << std::endl;
}
int main () {
std::deque<std::string> d{ "Hello", "Deque" };
function(d);
std::list<std::string> l{ "Hello", "List" };
function(l);
std::vector<std::string> v{ "Hello", "Vector" };
function(v);
return 0;
}
It is possible, but the direction you are going is not a wise one.
Instead, I would advise thinking about things via duck typing. If your function needs to iterate over the contents of the thing passed in, and expects the contents to be strings, what you are looking for is "this argument matches the concept of a sequence of std::strings".
That is best done in C++14 via the requires syntax, but you probably don't have C++14 compliant compilers yet, unless you are posting via time machine.
We can do this in C++11 via traits classes, SFINAE, and enable_if. Alternatively, we can do this in C++11 via traits classes and tag dispatching. Tag dispatching is less arcane than SFINAE, so I'll demonstrate it here.
We start with a generic function:
template<typename C>
void function( C const& c )
that accepts anything. Next, we write some traits class:
template<typename C,typename=void>
struct is_string_sequence : std::false_type {};
template<typename C>
struct is_string_sequence<C, typename std::enable_if<TODO STUFF HERE>::type> : std::true_type {};
that tells us if the argument passed in is a sequence over strings.
Next, we write a helper function:
template<typename C>
void function_helper( C const& c, std::true_type string_sequence_test ) {
// your code goes here, and can assume c is a string sequence
}
and forward the first one to it:
template<typename C>
void function( C const& c ) {
function_helper( c, is_string_sequence<C>() );
}
but we also create a tag to pass to the helper. This tag is true if our tests pass, and false if not. As we only have an override for true, the false case generates an error, and that error tends to be reasonably readable.
The only thing left is to write TODO STUFF HERE.
The duck-type way to tell if something is a string sequence in C++11 is to see if:
for( std::string s : c ) {
}
compiles. A way to do this is to see if begin and end free functions applied to c in a context where std::begin and std::end are visible returns iterators, and those iterators when dereferenced produce a type that is convertible to std::string.
This requires some gymnastics. First, seeing what begin and end result in when called in that particular context:
namespace adl_helper {
using std::begin; using std::end;
template<typename C>
auto adl_begin( C&& c )->decltype( begin(std::forward<C>(c)) );
template<typename C>
auto adl_end( C&& c )->decltype( end(std::forward<C>(c)) );
}
using adl_helper::adl_begin; using adl_helper::adl_end;
note that these have no bodies: for our purposes, we don't need bodies, we just need the return types.
Now we can ask "is something a container?"
template<typename C, typename=void>
struct is_container : std::false_type {};
template<typename C>
struct is_container<C, typename=typename std::enable_if<
std::is_convertible<
decltype( adl_begin( std::declval<C&>() ) == adl_end( std::declval<C&>() ) ),
bool
>::value
>::type> : std::true_type
{
typedef decltype( adl_begin( std::declval<C&>() ) ) iterator;
};
a fancier one might probe into iterator_traits instead (above, I just check that the begin and end work, and the results have an overridden == between them that returns something that can be converted to bool).
We can then ask if the resulting iterator has a value type that can be converted to std::string in another traits class.
Alternatively we can do a decltype( *adl_begin( std::declval<C&>() ) ), and see if that can be directly converted to std::string, and assume if begin works so will end.
The SFINAE method is similar, except instead of doing the tag dispatching to a helper, we put the test directly into the function signature, wrapped in an enable_if that activates or deactivates the function for overload resolution. This generates slightly harder to read errors but at a higher point in the call stack in my experience.
You don't do the approach you where aiming at, because the details of how the container was built from other types are not important: what is important is how you use it.
If you need other features form your type other than iteration, you can write other duck-type tests that determine if you can erase, insert, etc. However, note that associative containers and sequential containers are very different things, and treating them uniformly is rarely a good idea, other than in that both are sequences.
std::vector is an unstable container, i.e. by resizing the vector, iterators might become invalidated.
In contrast, std::list or boost::container::stable_vector are stable containers which keep the iterators valid until the removal of the corresponding element.
Is there a way to check whether a given container is stable? For instance, if I have something like
template<template <typename A, typename B=std::allocator<A> > class T=std::list>
class Foo
{
}
Is it possible to allow only for stable containers and forbid the unstable ones?
I don't think there is anything available providing such information, but you could write your own trait. However, you will need to specialize it for every stable container that may be used, which is perhaps not an option.
#include <boost/container/vector.hpp>
#include <iostream>
#include <type_traits>
#include <list>
#include <vector>
template <template <typename...> class Container>
struct is_stable
: std::false_type
{};
template <>
struct is_stable<std::list>
: std::true_type
{};
template <>
struct is_stable<boost::container::stable_vector>
: std::true_type
{};
template<template <typename...> class Container = std::list>
class Foo
{
static_assert(is_stable<Container>::value, "Container must be stable");
};
int main()
{
Foo<std::list> f1; // ok
Foo<std::vector> f2; // compiler error
}
I don't think there is a way you can automatically detect that a container is stable, without resorting to manual specialization.
Just for fun, I tried writing what the concept/axiom for stability would look like (concepts and axioms are an extension to the language that were considered for inclusion in C++11):
concept StableGroup<typename C, typename Op>
: Container<C>
{
void operator()(Op, C, C::value_type);
axiom Stability(C c, Op op, C::size_type index, C::value_type val)
{
if (index <= c.size())
{
auto it = std::advance(c.begin(), index);
op(c, val);
return it;
}
<->
if (index <= c.size())
{
op(c, val);
return std::advance(c.begin(), index);
}
}
}
If think this correctly captures the requirement that every iterator over the original container is equivalent to the corresponding iterator over the modified container. Not sure this is very useful, but coming up with such axioms is an interesting exercise :)!