Let's suppose I have a vector of integers and want to process it in an odd recursive manner (this situation might sound strange without a context, but still).
I want to use const_iterators to track current position. Here is odd_recursive_stuff() prototype:
// Note: changing "std::vector<int>::const_iterator& it_cur"
// into "std::vector<int>::const_iterator it_cur" will change
// the side effects!
void odd_recursive_stuff (std::vector<int>::const_iterator& it_cur,
std::vector<int>::const_iterator it_end);
Firstly I tried to call it this way:
void process_vec (const std::vector<int> &vec) {
odd_recursive_stuff (std::begin(vec), std::end(vec));
}
Luckily, It doesn't compile (in clang 8.0.0 for example):
Error: no matching function for call to 'recursive_odd_stuff'
Candidate function not viable: expects an l-value for 1st argument!
Because std::begin() returns r-value, so I have to call it another way that works:
void process_vec (const std::vector<int> &vec) {
std::vector<int>::const_iterator it_beg = std::begin (vec);
recursive_odd_stuff (it_beg, std::end(vec));
}
Now I'm wondering if it is possible to call the base of recursive_odd_stuff() with a single line without local_variable it_beg?
It seems that It is impossible to write another version of begin() which returns l-value, because "the return value of a function is an l-value if and only if it is a reference (C++03). (5.2.2 [expr.call] / 10)". So the only way is to call it with two lines?
So, there's a way to make it a one-liner, but I don't recommend it:
#include <vector>
#include <functional>
void odd_recursive_stuff (std::vector<int>::const_iterator& it_cur,
std::vector<int>::const_iterator it_end){}
void process_vec (const std::vector<int> &vec) {
odd_recursive_stuff ([it=std::begin(vec)]()mutable{return std::ref(it);}(), std::end(vec));
}
I think that your n-th recursive call changes the reference which is then used by the n-1th caller to do something. In this case, I would recommend splitting the code into two functions:
odd_recursive_stuff(IT begin, IT end){
odd_recursive_stuff_impl(begin, end);
}
odd_recursive_stuff_impl(IT& begin, IT& end){
....
}
This exposes a public interface that just requires iterators. Later when you change the algorithm in a way that does not require the reference, or it will require end to be a reference too, then you don't have to change all calls.
The first solution might expand into something akin to this:
void process_vec (const std::vector<int> &vec) {
using IT = std::vector<int>::const_iterator;
struct _lambda_type{
_lambda_type(const IT& it):_it(it){}
//By default lambda's () is const method, hence the mutable qualifier.
std::reference_wrapper<IT> operator()()/*Not const*/{
return std::ref(_it);
}
private:
IT _it;
};
//Previous lines...
{//The line with the call.
//Lambda is created before the call and lives until the expression is fully evaluated.
_lambda_type lambda{std::begin(vec)};
odd_recursive_stuff (lambda(), std::end(vec));
}//Here's the lambda destroyed. So the call is perfectly safe.
//The rest...
}
The lambda's operator() returns a reference to a local variable, but it's local to the lambda object, not the operator() itself. Because the lambda object lives until the end of the expression(;) the call is safe. Just note that I used std::ref as a quick way to return a reference without the need to mention the return type explicitly. std::reference_wrapper<T> is then implicitly convertible to T&.
return it; would return by value and [it=std::begin(vec)]()mutable ->decltype(it)&{...}; is not possible either. ->decltype(std::begin(vec))&{ works but it's wordy. Another alternatives are to write the iterator's type explicitly or use a using but that's even worse.
Overloading!
Have a version that accepts only rvalues:
void odd_recursive_stuff (std::vector<int>::const_iterator&& it_cur,
std::vector<int>::const_iterator it_end);
…and a version that accepts lvalue-references (and does the extra line for you):
void odd_recursive_stuff (const std::vector<int>::const_iterator& it_cur,
std::vector<int>::const_iterator it_end)
{
std::vector<int>::const_iterator it_copy(it_cur);
odd_recursive_stuff(std::move(it_copy), it_end);
}
This is the same principle upon which move semantics are based, because copy- and move-constructors are selected in the same way.
But you might consider dropping this whole thing, and just returning the new value of it instead:
std::vector<int>::const_iterator
odd_recursive_stuff(std::vector<int>::const_iterator it_cur,
std::vector<int>::const_iterator it_end);
You are then free to discard it at will.
Nobody really expects an iterator to be taken by reference.
Related
I have a vector that looks like this.
std::vector<std::pair<int,int> > v;
and I want to pass it into a function and make it so it's constant.
first I tried
void fun(const std::vector<std::pair<int,int> > v)
but that function allowed me to have the line
std::pair<int,int> p = v[i];
which I thought should have thrown an error since pair is not of type const. Then I realized that it's only the pointers that are being declared constant in vector so I tried
void fun(const std::vector<const std::pair<int,int> > v)
but that throws the error about there being no conversion.
I'm sure there's some inner workings that I'm not understanding that makes this illegal but would love some insight. Thanks.
I think you confuse reference and value semantics here. Lets the first function signature you tested:
void fun(const std::vector<std::pair<int,int> > v)
This copies the function argument into a new object v, which is additionaly const-qualified. Such signatures seldomly make sense: either you want to pass it as a const reference (to avoid copying) or you want to pass it by non-const value, because the function body will mutate the argument, but shall operate on its only copy. Besides, this snippet:
std::pair<int,int> p = v[i];
compiles just fine with the above function signature, because it copies the vector element at position i into a new pair object. The latter can be mutated, but this doesn't affect the vector at all.
Let's now consider the second function signature:
void fun(const std::vector<const std::pair<int,int> > v)
The same as before still applies here, and in addition, std::vector<const T> isn't useful, see this thread for an explanation.
How to fix it? If you don't want to copy the argument, but the function doesn't modify the vector, pass it by const reference. If the function modifies the vector and these modifications shall be visible at the call side, pass it as non-const reference. If the function modifies the vector but that shall happen independently of the call side vector, pass by non-const value.
void fun(const std::vector<std::pair<int,int> > v)
{
////
std::pair<int,int> p = v[i];
}
https://en.cppreference.com/w/cpp/container/vector/operator_at
No error will happen here because std::vector has overloaded operator[] both for const and non-const instances. In your case it will be resolved to const version and return const & to underlying i element.
Consider following code:
void fun(const std::vector<std::pair<int,int> > v)
{
////
std::pair<int,int> p = v[i]; //OK
v[i] = std::pair<int,int>{10,20}; //Compile error, trying to modify const data!
}
I am trying to add a reference to std algorithm. How can I edit my code to make it working?
double f(const std::vector<double> &arr, bool maxElem)
{
auto me = maxElem ? std::max_element : std::min_element;
//...
x = me(arr.begin(), arr.end());
//...
}
Your functions are template functions so you have to specify the template parameter. In this case, using a std::vector you need to pass them iterators:
Also, to cope with different potential overloads of the functions we should cast them to the type we require (thnx to #ChristianHackl):
double f(const std::vector<double>& arr, bool maxElem)
{
// deduce the iterator parameter types
using Iterator = decltype(arr.begin());
// select the overload type
using Overload = Iterator(*)(Iterator, Iterator);
auto me = maxElem
? static_cast<Overload>(std::max_element<Iterator>)
: static_cast<Overload>(std::min_element<Iterator>);
// need to dereference this because `me` returns an iterator
return *me(arr.begin(), arr.end());
}
Also note I dereference the return value from me() because it is an iterator (like a pointer).
Of course if your vector is empty that will dereference an invalid location so I recommend putting in a check:
double f(const std::vector<double>& arr, bool maxElem)
{
// Avoid Undefined Behavior
if(arr.empty())
throw std::runtime_error("empty vector not allowed");
// deduce the parameter types
using Iterator = decltype(arr.begin());
// select the overload type
using Overload = Iterator(*)(Iterator, Iterator);
auto me = maxElem
? static_cast<Overload>(std::max_element<Iterator>)
: static_cast<Overload>(std::min_element<Iterator>);
// need to dereference this because `me` returns an iterator
return *me(arr.begin(), arr.end());
}
Like this:
if (maxElem)
return std::max_element(arr.begin(), arr.end());
else
return std::min_element(arr.begin(), arr.end());
You could probably obtain a reference to the template specialisation of each "algorithm" you're trying to use, but due to the need to specify template arguments it would be messy and verbose, and thus a false economy. It may also carry a performance penalty (ruining inlineability).
It doesn't really make sense for f to behave like this in the first place; try to make your functions do one thing well. Arguably, their underlying semantics shouldn't depend on an argument like this.
So, in order to allow code such as
auto vect = ...;
auto it = vect.begin(), end = vect.end(); // want const_iterator, getting iterator
to pick the right overload of begin() and end(), even for non-const containers, the more explicit cbegin()/cend() functions were added.
Why stop there?
Associative containers have a find() method with the same problem. Sequence containers have front() and back(), again with the same problem.
Are these missing explicit const versions omissions, or by design?
A wider API has cost, even just to skip over it when looking for the function you want.
template<class T>
T const as_const(T&& t) noexcept(noexcept(T(std::declval<T>())) {
return std::forward<T>(t);
}
template<class T>
T const& as_const(T& t) noexcept {
return t;
}
does most of what you want. It would even make cbegin obsolete.
(modifications done to the code above based off n4380 supplied by #T.C below. Code differs, because I think n4380 got it slightly wrong in the T&& case.)
The purpose of cbegin/cend is to solve a specific problem. Consider this code:
std::vector<int> & v = //... v is a non-const reference
// For clarity, I want this iterator to be a const_iterator.
// This works because iterator is implicitly convertible to const_iterator
std::vector<int>::const_iterator iter = find(v.begin(),v.end(),42);
// (1) Now I want to use iter in an algorithm
std::find(iter, v.end(), 38); //Error, can not deduce the template parameter for Iter.
// (2) This works
std::find(iter, const_cast<const std::vector<int> &>(v).end(), 38);
// (3) This is the same as (2).
std::find(iter, v.cend(), 38);
The problem is that, due to how template deduction rules work, the compiler can not deduce the template iterator argument in the statement (1), because Container::iterator and Container::const_iterator are (potentially) two completely unrelated types (even if the former is implicitly convertible in the latter).
The statement (2) is not exactly a beautiful line, that is why we need cend().
Now, front(), back() et similia all return a reference. A non-const reference can always be deduced as const in a templated function, that is:
template<class T> void f( const T & l, const T & r);
int main()
{
int x; vector<int> v;
//This will works even if the return type of front() is int&.
f(x, v.front());
}
Since Container::const_reference is required by the standard to be equal to const Container::value_type &, cfront()/cback() do not buy us anything.
It is worth mentioning that other containers library (looking at you Qt) are implemented using Copy-On-Write.
This means that calling a const function on such container is potentially much less expensive than calling the equivalent non-const version, simply because the non-const might copy the entire container under the hood.
For this reason the Qt containers have a lot of constFunction in their interface, and the user has the freedom to pick the right one.
I generally prefer constness, but recently came across a conundrum with const iterators that shakes my const attitude annoys me about them:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'const_iterator's which then prevent me from doing anything with modifing the result (which makes sense).
Is the solution, then, to give up on making the passed in container reference const, or am I missing another possibility?
This has always been my secret reservation about const: That even if you use it correctly, it can create issues that it shouldn't where there is no good/clean solution, though I recognize that this is more specifically an issue between const and the iterator concept.
Edit: I am very aware of why you cannot and should not return a non-const iterator for a const container. My issue is that while I want a compile-time check for my container which is passed in by reference, I still want to find a way to pass back the position of something, and use it to modify the non-const version of the list. As mentioned in one of the answers it's possible to extract this concept of position via "advance", but messy/inefficient.
If I understand what you're saying correctly, you're trying to use const to indicate to the caller that your function will not modify the collection, but you want the caller (who may have a non-const reference to the collection) to be able to modify the collection using the iterator you return. If so, I don't think there's a clean solution for that, unless the container provides a mechanism for turning a const interator into a non-const one (I'm unaware of a container that does this). Your best bet is probably to have your function take a non-const reference. You may also be able to have 2 overloads of your function, one const and one non-const, so that in the case of a caller who has only a const reference, they will still be able to use your function.
It's not a trap; it's a feature. (:-)
In general, you can't return a non-const "handle" to your data from a const method. For example, the following code is illegal.
class Foo
{
public:
int& getX() const {return x;}
private:
int x;
};
If it were legal, then you could do something like this....
int main()
{
const Foo f;
f.getX() = 3; // changed value of const object
}
The designers of STL followed this convention with const-iterators.
In your case, what the const would buy you is the ability to call it on const collections. In which case, you wouldn't want the iterator returned to be modifiable. But you do want to allow it to be modifiable if the collection is non-const. So, you may want two interfaces:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
MyList::iterator find( MyList & list, int identifier )
{
// do some stuff to find identifier
return retItor;
}
Or, you can do it all with one template function
template<typename T>
T find(T start, T end, int identifier);
Then it will return a non-const iterator if the input iterators are non-const, and a const_iterator if they are const.
What I've done with wrapping standard algorithms, is have a metaobject for determining the type of container:
namespace detail
{
template <class Range>
struct Iterator
{
typedef typename Range::iterator type;
};
template <class Range>
struct Iterator<const Range>
{
typedef typename Range::const_iterator type;
};
}
This allows to provide a single implementation, e.g of find:
template <class Range, class Type>
typename detail::Iterator<Range>::type find(Range& range, const Type& value)
{
return std::find(range.begin(), range.end(), value);
}
However, this doesn't allow calling this with temporaries (I suppose I can live with it).
In any case, to return a modifiable reference to the container, apparently you can't make any guarantees what your function does or doesn't do with the container. So this noble principle indeed breaks down: don't get dogmatic about it.
I suppose const correctness is more of a service for the caller of your functions, rather that some baby-sitting measure that is supposed to make sure you get your simple find function right.
Another question is: how would you feel if I defined a following predicate and then abused the standard find_if algorithm to increment all the values up to the first value >= 3:
bool inc_if_less_than_3(int& a)
{
return a++ < 3;
}
(GCC doesn't stop me, but I couldn't tell if there's some undefined behaviour involved pedantically speaking.)
1) The container belongs to the user. Since allowing modification through the predicate in no way harms the algorithm, it should be up to the caller to decide how they use it.
2) This is hideous!!! Better implement find_if like this, to avoid this nightmare (best thing to do, since, apparently, you can't choose whether the iterator is const or not):
template <class Iter, class Pred>
Iter my_find_if(Iter first, Iter last, Pred fun)
{
while (first != last
&& !fun( const_cast<const typename std::iterator_traits<Iter>::value_type &>(*first)))
++first;
return first;
}
Although I think your design is a little confusing (as others have pointed iterators allow changes in the container, so I don't see your function really as const), there's a way to get an iterator out of a const_iterator. The efficiency depends on the kind of iterators.
#include <list>
int main()
{
typedef std::list<int> IntList;
typedef IntList::iterator Iter;
typedef IntList::const_iterator ConstIter;
IntList theList;
ConstIter cit = function_returning_const_iter(theList);
//Make non-const iter point to the same as the const iter.
Iter it(theList.begin());
std::advance(it, std::distance<ConstIter>(it, cit));
return 0;
}
Rather than trying to guarantee that the list won't be changed using the const keyword, it is better in this case to guarantee it using a postcondition. In other words, tell the user via comments that the list won't be changed.
Even better would be using a template that could be instantiated for iterators or const_iterators:
template <typename II> // II models InputIterator
II find(II first, int identifier) {
// do stuff
return iterator;
}
Of course, if you're going to go to that much trouble, you might as well expose the iterators of MyList to the user and use std::find.
If you're changing the data directed by the iterator, you're changing the list.
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'cons_iterator's which then prevent me from doing anything with the result.
What is "dong anything"? Modifying the data? That's changing the list, which is contradictory to your original intentions. If a list is const, it (and "it" includes its data) is constant.
If your function were to return a non-const iterator, it would create a way of modifying the list, hence the list wouldn't be const.
You are thinking about your design in the wrong way. Don't use const arguments to indicate what the function does - use them to describe the argument. In this case, it doesn't matter that find() doesn't change the list. What matters is that find() is intended to be used with modifiable lists.
If you want your find() function to return a non-const iterator, then it enables the caller to modify the container. It would be wrong for it to accept a const container, because that would provide the caller with a hidden way of removing the const-ness of the container.
Consider:
// Your function
MyList::iterator find(const MyList& list, int identifier);
// Caller has a const list.
const MyList list = ...
// but your function lets them modify it.
*( find(list,3) ) = 5;
So, your function should take a non-const argument:
MyList::iterator find(MyList& list, int identifier);
Now, when the caller tries to use your function to modify her const list, she'll get a compilation error. That's a much better outcome.
If you're going to return a non-const accessor to the container, make the function non-const as well. You're admitting the possibility of the container being changed by a side effect.
This is a good reason the standard algorithms take iterators rather than containers, so they can avoid this problem.
for_each accepts InputIterators :
//from c++ standard
template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function f);
is it ok to change the object in Function f, like this :
struct AddOne
{
void operator()(int & x){x = x + 1;}
};
std::vector<int> vec(10);
std::for_each(vec.begin(),vec.end(),AddOne());
This code works in VC++2008 and also with GCC, but is it also portable (legal) code ?
(InputIterators are only guaranteed to be usable as rvalue, in this case they are used as lvalue in AddOne's operator())
Read this article.
To be pedantic: for_each is a non-modifying sequence operation. The intent is not to modify the sequence. However, it is okay to modify the input sequence when using for_each.
You misunderstand something. Saying input iterators are only guaranteed to be usable as rvalues doesn't mean you can't get an lvalue out of an iterator somehow. So it does not mean that the result of *iterator is an rvalue. What you/for_each passes to AddOne is the result of operator* - not the iterator itself.
About for_each and a modifying function object - read this question
If in doubt use std::transform as it will convey even to the casual reader of your code that you intend to modify something.
It is legal - the input iterators are used to specify the range, not to do the processing.
Interestingly, Josuttis in his book "The C++ Standard Library" lists for_each as modifying, raher than non-modifying.
My suggestion would be, it all matters how you pass your item to the function i.e. whether by reference or pointer and change it or as a copy and change it.
But as other's have said, if you want to change the values, it's better to use transform as it cares about the return values.
class MultiplyBy {
private:
int factor;
public:
MultiplyBy(int x) : factor(x) {
}
int operator () (int other) const {
other = factor + other;
return other;
}
// int operator () (int & other) const {
// other = factor + other;
// return other;
// }
};
vector<int> x1= {1, 2, 3, 4, 5};
vector<int> x2;
std::transform(x1.begin(), x1.end(), back_inserter(x2), MultiplyBy(3));
std::for_each(x1.begin(), x1.end(), MultiplyBy(3));