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.
Related
I have a function with a template parameter T and would like to pass a value of type const T by reference.
The C++ compiler throws an error, (kind of) understandably so. Hence I was wondering if there exists a way to do this in a safe and concise way?
I created a very small example that reflects the issue I am having in my project.
(in my project the issue appears in a constant member function of some class, but from my experiments the issue should be "faithfully" reflected in the example below by use of a constant variable of int instead for simplicity's sake).
I am aware that I could theoretically use a separate template parameter "cT", but that would be horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument ...
I also understand that I could simply refrain from using templates at all and just specify this for every type.
I was just wondering if what I am trying to achieve below can be done with templates.
Thanks and have a nice day! :)
template<typename T>
bool ContainsElement(std::list<T>& findList, const T& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
int main()
{
std::list<int*> myList;
const int testConst = 6;
auto pointerToTestConst = &testConst;
ContainsElement(myList, pointerToTestConst); // compiler screams
}
The issue is in incompatibility between pointers:
pointerToTestConst is of type const int* - non-const pointer to const integer. Therefore T=const int*
myList is of type list<int*>, deducing T=int*.
Since those types are not the same, compilation fails. Rightfully so, because elem would allow changing testConst if T=int*.
The issue manifests regardles you passing elem by (const) reference or value.
horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument
So what? They get a compiler error about the comparison. It is no more unsafe than your code right now. I would argue that a generic ContainsElement should not care what you pass to is as long as it compares equal to some element in the list, it is a match.
Of course STL already offers this for std::find and std::ranges::find which also does not care and it is not called unsafe because of it.
To "normalize" the type for pointers and non-pointers, std::conditional may be used, finally your template still defines single T parameter:
#include <type_traits>
template<typename T>
bool ContainsElement(std::list<T>& findList,
const std::conditional_t<
std::is_pointer<T>::value,
std::add_pointer_t<
std::add_const_t<
std::remove_pointer_t<T>
>
>,
std::add_const_t<T>
>& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
Above example uses helper types with postfix _t introduced in C++14, for C++11, use std::conditional::type.
It works for std::list<int> and std::list<int*>.
Of course in case of std::list<int*> it will check whether list contains exactly the same pointer address, not the pointed value.
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.
Inside an algorithm, I want to create a lambda that accepts an element by reference-to-const:
template<typename Iterator>
void solve_world_hunger(Iterator it)
{
auto lambda = [](const decltype(*it)& x){
auto y = x; // this should work
x = x; // this should fail
};
}
The compiler does not like this code:
Error: »const«-qualifier cannot be applied to »int&« (translated manually from German)
Then I realized that decltype(*it) is already a reference, and of course those cannot be made const. If I remove the const, the code compiles, but I want x = x to fail.
Let us trust the programmer (which is me) for a minute and get rid of the const and the explicit &, which gets dropped due to reference collapsing rules, anyways. But wait, is decltype(*it) actually guaranteed to be a reference, or should I add the explicit & to be on the safe side?
If we do not trust the programmer, I can think two solutions to solve the problem:
(const typename std::remove_reference<decltype(*it)>::type& x)
(const typename std::iterator_traits<Iterator>::value_type& x)
You can decide for yourself which one is uglier. Ideally, I would want a solution that does not involve any template meta-programming, because my target audience has never heard of that before. So:
Question 1: Is decltype(*it)& always the same as decltype(*it)?
Question 2: How can I pass an element by reference-to-const without template meta-programming?
Question 1: no, the requirement on InputIterator is merely that *it is convertible to T (table 72, in "Iterator requirements").
So decltype(*it) could for example be const char& for an iterator whose value_type is int. Or it could be int. Or double.
Using iterator_traits is not equivalent to using decltype, decide which you want.
For the same reason, auto value = *it; does not necessarily give you a variable with the value type of the iterator.
Question 2: might depend what you mean by template meta-programming.
If using a traits type is TMP, then there's no way of specifying "const reference to the value type of an iterator" without TMP, because iterator_traits is the only means to access the value type of an arbitrary iterator.
If you want to const-ify the decltype then how about this?
template<typename Iterator>
void solve_world_hunger(Iterator it)
{
const auto ret_type = *it;
auto lambda = [](decltype(ret_type)& x){
auto y = x; // this should work
x = x; // this should fail
};
}
You might have to capture ret_type in order to use its type, I can't easily check at the moment.
Unfortunately it dereferences the iterator an extra time. You could probably write some clever code to avoid that, but the clever code would end up being an alternative version of remove_reference, hence TMP.
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));