When using boost::any_range, what's the correct way of specifying that the underlying container (if any) shouldn't be modified?
E.g., with the alias
template<typename T>
using Range = boost::any_range<T, boost::forward_traversal_tag>;
to declare a range that isn't capable of modifying the contents of the underlying container or "data source", should it be declared as
const Range<T> myRange;
or as
Range<const T> myRange;
?
I suspect the first version is the correct one. But is it guaranteed to keep the constness of the container, if, for example, I apply any of the boost::adaptors?
Edit
From the documentation, apparently the range_iterator metafunction "deduces" the constness of the underlying container by declaring the range with const T instead of T. That is, range_iterator::<const T>::type is const_iterator (if the underlying container has such member type), instead of iterator, so the container can't be modified through this iterator.
Does that mean that Range<const T> also uses const_iterators to traverse the range?
Apparently the correct way to ensure that the values aren't modified is neither of those I mentioned.
From Boost.Range's documentation, we can see that any_range takes the following template parameters:
template<
class Value
, class Traversal
, class Reference
, class Difference
, class Buffer = any_iterator_default_buffer
>
class any_range;
I strongly suspect the way to declare a "const range" is to specify const T as the Reference type template parameter, although, surprisingly, I still haven't been able to find any explicit indication in the documentation that this is so.
So a const range could be declared as:
template<class C>
using ConstRange = boost::any_range<C, boost::forward_traversal_tag, const C, std::ptrdiff_t>
Related
The struct Vec has all the requirements to be std::default_initializable. But the declaration is not finished, so it failed to compile.
template <std::default_initializable A>
struct It {};
struct Vec {
using Iterator = It<Vec>;
};
Is there some sort of workaround to keep the It requirement ?
In general, the answer to your question is no. If you want to make a member type alias, it has to be known at the time in question. And if the template you're trying to instantiate requires that the type it is given is complete (which default-initializable does), then it must be complete.
In your specific case however, it's not really necessary to make Iterator a member. If you want the iterator type for a range, the correct way to do that is to use ranges::iterator_t. And that meta-function will return the type that ranges::begin() returns.
Your begin member function can be specified to return auto, such that the definition of the function is what provides the It<Vec>, as follows:
template <std::default_initializable A>
struct It {};
struct Vec
{
auto begin() {return It<Vec>(...);}
};
That having been said, even if you want to define your iterator type outside of the container/range that it serves, it is still bound to that container/range type. Unless It is some kind of view or range transformation (and if it is, it probably shouldn't be default-constructing the range it modifies), It<UserType> shouldn't work. As such, constraining the It template is pointless; you know that Vec is default constructible because you wrote that class just below the iterator. If you want a sanity check, you can use a static_assert, but you don't need to constrain the template itself.
This question cites the C++ standard to demonstrate that the alignment and size of CV qualified types must be the same as the non-CV qualified equivalent type. This seems obvious, because we can implicitly cast an object of type T to a const T& using static_cast or reinterpret_cast.
However, suppose we have two types which both have the same member variable types, except one has all const member variables and the other does not. Such as:
typedef std::pair<T, T> mutable_pair;
typedef std::pair<const T, const T> const_pair;
Here, the standard does not allow us to produce a const_pair& from an instance of mutable_pair. That is, we cannot say:
mutable_pair p;
const_pair& cp = reinterpret_cast<const_pair&>(p);
This would yield undefined behavior, as it is not listed as a valid use of reinterpret_cast in the standard. Yet, there seems to be no reason, conceptually, why this shouldn't be allowed.
So... why should anyone care? You can always just say:
const mutable_pair& cp = p;
Well, you might care in the event you only want ONE member to be const qualified. Such as:
typedef std::pair<T, U> pair;
typedef std::pair<const T, U> const_first_pair;
pair p;
const_first_pair& cp = reinterpret_cast<const_first_pair&>(p);
Obviously that is still undefined behavior. Yet, since CV qualified types must have the same size and alignment, there's no conceptual reason this should be undefined.
So, is there some reason the standard doesn't allow it? Or is it simply a matter that the standard committee didn't think of this use case?
For anyone wondering what sort of use this could have: in my particular case, I ran into a use case where it would have been very useful to be able to cast a std::pair<T, U> to a std::pair<const T, U>&. I was implementing a specialized balanced tree data structure that provides log(N) lookup by key, but internally stores multiple elements per node. The find/insert/rebalance routines requires internal shuffling of data elements. (The data structure is known as a T-tree.) Since internal shuffling of data elements adversely affects performance by triggering countless copy constructors, it is beneficial to implement the internal data shuffling to take advantage of move constructors if possible.
Unfortunately... I also would have liked to be able to provide an interface which meets the C++ standard requirements for AssociativeContainer, which requires a value_type of std::pair<const Key, Data>. Note the const. This means individual pair objects cannot be moved (or at least the keys can't). They have to be copied, because the key is stored as a const object.
To get around this, I would have liked to be able to store elements internally as mutable objects, but simply cast the key to a const reference when the user access them via an iterator. Unfortunately, I can't cast a std::pair<Key, Data> to a std::pair<const Key, Data>&. And I can't provide some kind of workaround that returns a wrapper class or something, because that wouldn't meet the requirements for AssociativeContainer.
Hence this question.
So again, given that the size and alignment requirements of a CV qualified type must be the same as the non-CV qualified equivalent type, is there any conceptual reason why such a cast shouldn't be allowed? Or is it simply something the standard writers didn't really think about?
Having a type as a template parameter does not mean that you won't have different alignments, the class contents could be changed, e.g., via specialization or template metaprogramming. Consider:
template<typename T> struct X { int i; };
template<typename T> struct X<const T> { double i; };
template<typename T> struct Y {
typename std::conditional<std::is_const<T>::value, int, double>::type x;
};
I'm making a simple, non-owning array view class:
template <typename T>
class array_view {
T* data_;
size_t len_;
// ...
};
I want to construct it from any container that has data() and size() member functions, but SFINAE-d correctly such that array_view is only constructible from some container C if it would then be valid and safe behavior to actually traverse data_.
I went with:
template <typename C,
typename D = decltype(std::declval<C>().data()),
typename = std::enable_if_t<
std::is_convertible<D, T*>::value &&
std::is_same<std::remove_cv_t<T>,
std::remove_cv_t<std::remove_pointer_t<D>>>::value>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
That seems wholly unsatisfying and I'm not even sure it's correct. Am I correctly including all the right containers and excluding all the wrong ones? Is there an easier way to write this requirement?
If we take a look at the proposed std::experimental::array_view in N4512, we find the following Viewable requirement in Table 104:
Expression Return type Operational semantics
v.size() Convertible to ptrdiff_t
v.data() Type T* such that T* is static_cast(v.data()) points to a
implicitly convertible to U*, contiguous sequence of at least
and is_same_v<remove_cv_t<T>, v.size() objects of (possibly
remove_cv_t<U>> is true. cv-qualified) type remove_cv_t<U>.
That is, the authors are using essentially the same check for .data(), but add another one for .size().
In order to use pointer arithmetic on U by using operations with T, the types need to be similar according to [expr.add]p6. Similarity is defined for qualification conversions, this is why checking for implicit convertibility and then checking similarity (via the is_same) is sufficient for pointer arithmetic.
Of course, there's no guarantee for the operational semantics.
In the Standard Library, the only contiguous containers are std::array and std::vector. There's also std::basic_string which has a .data() member, but std::initializer_list does not, despite it being contiguous.
All of the .data() member functions are specified for each individual class, but they all return an actual pointer (no iterator, no proxy).
This means that checking for the existence of .data() is currently sufficient for Standard Library containers; you'd want to add a check for convertibility to make array_view less greedy (e.g. array_view<int> rejecting some char* data()).
The implementation can of course be moved away from the interface; you could use Concepts, a concepts emulation, or simply enable_if with an appropriate type function. E.g.
template<typename T, typename As,
typename size_rt = decltype(std::declval<T>().size())
typename data_rt = decltype(std::declval<T>().data())>
constexpr bool is_viewable =
std::is_convertible_v<size_rt, std::ptrdiff_t>
&& std::is_convertible_v<data_rt, T*>
&& std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<data_rt>>;
template <typename C,
typename = std::enable_if_t<is_viewable<C, T>>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
And yes, that doesn't follow the usual technique for a type function, but it is shorter and you get the idea.
Suppose I have the following definitions
template <class T>
class Sequence
{
}
E.g
Sequence<string> might be an array of strings, similar to vector<string>
// Now define iterator template
template <class T>
class SequenceIterator
{
}
The idea here of course is to be able to create an iterator over some sequence
E.g.
SequenceIterator< Sequence<string> > iter1;
SequenceIterator< Sequence<int> > iter2;
The question I now have is how to define the member function that would exist inside SequenceIterator and whose purpose is to return the next value in the sequence. Typically I would expect to write that as
bool Next(T1 & value); // If the iterator has not finished, set value to the next item
However, the SequenceIterator has been passed in a templated name already, i.e, Sequence<string> or Sequence<int>
So the question is, how do I generically refer to that underlying type (string or int) so that I can define the Next member function.
Thanks,
David
There are three ways: change the definition of Sequence to include
typedef T type;
or, change the template parameters for SequenceIterator to explicitly recognize that Sequence is a template itself
template< template < class > class Seq, class T >
class SequenceIterator< Seq< T > >
and while the instantiation of SequenceIterator does not change, you can now access T directly. Thirdly, you can use a container traits class that handles the type deduction for you. The third option provides the least coupling between Sequence and SequenceIterator, but, like Mark said, the standard containers tend to use the first method.
The standard library solves this by having typedefs within every container. In this case Sequence<T> would have a typedef T value_type; so you can then use Sequence<T>::value_type to refer to that type.
Also, I would highly consider using operator++ and operator* like the standard library so you don't confuse people with a non-standard-like iterator interface.
I have a class
template <typename Iterator, typename Value>
class Foo {
public:
Foo(const Iterator& it) { ... }
...
private:
map<Value, int> m_;
}
};
Is there any way to get rid of Value in the template? The Iterator may or may not be an STL iterator, but it's guaranteed that *it type is Value.
I know about iterator_traits<T>::value_type for STL iterators, but wonder if there's any way to get Value type automatically for an arbitrary Iterator type?
One trick I'm thinking about - say, we have a helper class
template <typename Iterator, typename Value>
class Bar {
public:
Bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
...
};
Then if we instantiate Bar as Bar(it, *it), the type of Value will be known inside Bar. But I can't find a good way to combine Bar with Foo.
Any iterator should provide iterator_traits<Iterator>::value_type. If it does not, then it is not an iterator. ISO C++ 2003 24.3.1[lib.iterator.traits] "Iterator traits":
To implement algorithms only in terms
of iterators, it is often necessary to
determine the value and difference
types that correspond to a particular
iterator type. Accordingly, it is
required that if Iterator is the type
of an iterator, the types
iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category
be defined as the iterator’s
difference type, value type and
iterator category, respectively.
Aside from that, there's no general way to obtain a type of an arbitrary C++ expression. C++0x will rectify it by providing decltype.
Sorry. The correct way to get rid of Value is to use iterator_traits as you suggested.
If your non-STL iterator is a naked pointer, then you get correct iterator_traits typedefs for free. Otherwise the non-STL iterator class must define the correct typedefs.
See the iterator traits documentation for more information.
As to getting value type of iterator previous answers were correct.
But there is more. The trick you are thinking of would not work with class. If Bar was a function like:
template <typename Iterator, typename Value>
void bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
then type deduction would work for bar(it, *it) and you would have the value type inside of bar. (But keep in mind that to use this trick you would still have to have a dereferencable iterator it which is not always good - how to deal with empty sequence then?)
Using a class Bar you would have to provide the template arguments Iterator and Value manually as there is no type deduction for classes and using Bar(it, *it) would no compile.