I wrote a little (working) test code but I do not understand why in the test1 function I can only pass a int* const as parameter while in the test2 function I can pass a const int*. If I pass a const int* to test1, I get a discard qualifier error.
In my research, I found that both std::find and set::find have a const version so I can't see why they behave differently. I also tried with boost::container::flat_set instead of a std::set and I got the same result.
Could someone explain me please?
class myClass
{
public:
myClass() {};
~myClass() {};
void add(int* ref)
{
this->_ref.insert(ref);
};
bool test1(int* const ref) const
{
return ( this->_ref.find(ref) != this->_ref.end() );
}
inline
bool test2(const int* ref) const
{
return ( std::find(this->_ref.begin(), this->_ref.end(), ref) != this->_ref.end() );
}
std::set<int*> _ref;
};
int main()
{
myClass test;
test.add(new int(18));
test.add(new int(35));
test.add(new int(78));
test.add(new int(156));
std::cout<<test.test1(0)<<std::endl;
std::cout<<test.test1(*test._ref.begin())<<std::endl;
std::cout<<test.test2(0)<<std::endl;
std::cout<<test.test2(*test._ref.begin())<<std::endl;
return 0;
}
set::find() gives the answer in O(logN) while std::find() gives the answer in O(N) .
Similarly, map::find() gives the answer in O(logN) while std::find() gives the answer in O(N) .
The container std::set<int*> has only homogeneous lookup, so you can only search keys by comparing them with a value of the same type: find, count, erase. Naturally, a value of type const int* does not have the same type as int*, so your test2 code attempts to convert the former to the latter, which is not an allowed conversion.
The fact that containers could only be used in a homogeneous way like that has been a shortcoming of C++ since inception, and more egregrious examples of undesired conversions are when you have a map with std::string keys and want to look up an element with a key provided as a string literal. You always have to construct the dynamic std::string object, even though std::string provide comparisons operators with string literals.
Therefore, since C++14, you can also make a set (or map) with inhomogeneous lookup by spelling it std::set<int*, std::less<>>. With such a container, the loopup functions become templates, and you can indeed compare values of different types (leaving the conversion logic to the underlying <-operator). But note that std::less<int*> is required to provide a strict weak ordering on pointers, whereas std::less<> is not, so you may end up with undefined behaviour.
Related
I'm writing an application with a high performance thread that doesn't allow allocation. I have a map that looks like this:
map<String, MyCustomClass> objectCollection;
Where String is a custom wrapper around std::string. I want to be able to write code like this on the high priority thread:
int someValue = objectCollection["some string"].value;
When I do this, indexing into the array causes the construction of a String, which requires allocation. My thought was that I might be able to define a custom comparator for my map that would accept a const char*, and be able to do string comparison with a String's c string guts. Is this possible? How might it look?
I can do something like this with String instances:
String strTest = "";
const char* chars = strTest.chars();
You can get away with doing only one allocation.
static const string Key("some string");
int someValue = objectCollection[Key];
Doing it with zero allocations would require a different string class. You would have somehow make use of const char* and a custom comparison mechanism.
A custom comparison won't do you any good with a map; the lookup operator always converts its argument to the key type, regardless of how the comparison operator works. But when you want fast lookups, there's probably a better way.
Keeping things in a sorted vector and looking them up using the binary search algorithms (lower_bound() etc) is usually faster than looking them up in a map (because, among other things, a map's internal tree structure imposes a good deal of pointer chasing on each lookup). A map is much faster for insertion than a sorted vector, but when fast lookup is more important than fast insertion, the vector is usually faster, and the vector has the advantage that you can use a heterogeneous comparison function (one that takes two different argument types).
Something like this:
struct Element {
std::string key;
Thing value;
};
bool compare(const Element& lhs, const char* rhs) {
return lhs.key < rhs;
}
using Collection = std::vector<Element>;
inline Thing lookup(const char* key, const Collection& coll) {
// Requires coll to be already sorted
auto i(std::lower_bound(coll.begin(), coll.end(), key, compare));
if (i != coll.end() && i->key == key)
return i->value;
else
return Thing();
}
In C++14, there are some neat new features that should allow this to happen. For instance, there's a templated map::find
template< class K > iterator find( const K& x );
http://en.cppreference.com/w/cpp/container/map/find
All you can do is change the key_type to const char* since map::find aswell as map::operator[] aswell as map::at take key_type as their argument. as such even if you pass a const char* it will construct a String before the map function is even called. So unless you make your String static you wont get away without constructing one.
The type vector<char *> is not convertible to const vector<const char*>. For example, the following gives a compilation error:
#include <vector>
using namespace std;
void fn(const vector<const char*> cvcc)
{
}
int main()
{
vector<char *> vc = vector<char *>();
fn(vc);
}
I understand why vector<char*> is not convertable to vector<const char*> - extra members of type const char * may be added to the vector, and afterwards they would be accessible as non-const. However, if the vector itself is const, this can't happen.
My best guess is that this would be harmless, but there is no way the compiler is allowed to deduce that this would be harmless.
How can this be worked around?
This question was suggested by the C++ FQA here.
In general, C++ does not allow you to cast someclass<T> to someclass<U> as a template someclass might be specialized for U. It doesn't matter how T and U are related. This mean that the implementation and thus the object layout might be different.
Sadly, there is no way to tell the compiler in which cases the layout and/or behaviour didn't change and the cast should be accepted. I imagine it could be very useful for std::shared_ptr and other use-cases, but that is not going to happen anytime soon (AFAIK).
void fn(const vector<const char*>)
As the top-level const qualifier is dropped for the function type, this is (at the call site) equivalent to:
void fn(vector<const char*>)
Both of which request a copy of the passed vector, because Standard Library containers follow value semantics.
You can either:
call it via fn({vc.begin(), vc.end()}), requesting an explicit conversion
change the signature to, e.g. void fn(vector<const char*> const&), i.e. taking a reference
If you can modify the signature of fn, you can follow GManNickG's advice and use iterators / a range instead:
#include <iostream>
template<typename ConstRaIt>
void fn(ConstRaIt begin, ConstRaIt end)
{
for(; begin != end; ++begin)
{
std::cout << *begin << std::endl;
}
}
#include <vector>
int main()
{
char arr[] = "hello world";
std::vector<char *> vc;
for(char& c : arr) vc.push_back(&c);
fn(begin(vc), end(vc));
}
This gives the beautiful output
hello world
ello world
llo world
lo world
o world
world
world
orld
rld
ld
d
The fundamental issue is to pass around Standard Library containers. If you only need constant access to the data, you don't need to know the actual container type and can use the template instead. This removes the coupling of fn to the type of container the caller uses.
As you have noticed, it's a bad idea to allow access of a std::vector<T*> through a std::vector<const T*>&. But if you don't need to modify the container, you can use a range instead.
If the function fn shall not or cannot be a template, you could still pass around ranges of const char* instead of vectors of const char. This will work with any container that guarantees contiguous storage, such as raw arrays, std::arrays, std::vectors and std::strings.
To work around the issue, you could implement your own template wrapper:
template <typename T> class const_ptr_vector;
template <typename T> class const_ptr_vector<T *> {
const std::vector<T *> &v_;
public:
const_ptr_vector (const std::vector<T *> &v) : v_(v) {}
typedef const T * value_type;
//...
value_type operator [] (int i) const { return v_[i]; }
//...
operator std::vector<const T *> () const {
return std::vector<const T *>(v_.begin(), v_.end());
}
//...
};
The idea is that the wrapper provides an interface to the referenced vector, but the return values are always const T *. Since the referenced vector is const, all the interfaces provided by the wrapper should be const as well, as illustrated by the [] operator. This includes the iterators that would be provided by the wrapper. The wrapper iterators would just contain an iterator to the referenced vector, but all operations that yield the pointer value would be a const T *.
The goal of this workaround is not to provide something that can be passed to a function wanting a const std::vector<const T *> &, but to provide a different type to use for the function that provides the type safety of such a vector.
void fn(const_ptr_vector<char *> cvcc)
{
}
While you could not pass this to a function expecting a const std::vector<const T *> &, you could implement a conversion operator that would return a copy of a std::vector<const T *> initialized with the pointer values from the underlying vector. This has also been illustrated in the above example.
As the FQA suggests, this is a fundamental flaw in C++.
It appears that you can do what you want by some explicit casting:
vector<char*> vc = vector<char *>();
vector<const char*>* vcc = reinterpret_cast<vector<const char*>*>(&vc);
fn(*vcc);
This invokes Undefined Behavior and is not guaranteed to work; however, I am almost certain it will work in gcc with strict aliasing turned off (-fno-strict-aliasing). In any case, this can only work as a temporary hack; you should just copy the vector to do what you want in a guaranteed manner.
std::copy(vc.begin(), vc.end(), std::back_inserter(vcc));
This is OK also from the performance perspective, because fn copies its parameter when it's called.
I am playing around with the new explicit for cast-operators. If you write something like
struct Data {
explicit operator string();
};
It is not possible to accidentally convert Data to string. The darget data type bool is an exception: In certain cases the implicit conversion is allowed even if it is marked explicit -- contextual conversion. So, you can use this data types in an if(...) for example:
struct Ok {
explicit operator bool(); // allowed in if(...) anyway
};
The paragraph "25.4.(2) Sorting and related operations" seems to allow this for the Compare functor of standard containers like set as well. But my tries with gcc-4.7.0 fail, and I am note sure if it is my mis-understanding or a bug in gcc?
#include <set>
struct YesNo { // Return value type of Comperator
int val_;
explicit YesNo(int y) : val_{y} {}
/* explicit */ operator bool() { return val_!=0; }
};
static const YesNo yes{1};
static const YesNo no{0};
struct LessYesNo { // Comperator with special return values
YesNo operator()(int a, int b) const {
return a<b ? yes : no;
}
};
int main() {
std::set<int,LessYesNo> data {2,3,4,1,2};
}
Without the explicit before operator bool() the example compiles. And my understanding of "25.4.(2)" is, that this should also compile with the `explicit.
Did I understand the Std correctly that for set also explicit bool conversions should work? And might this be a bug in gcc then, or did I understand something wrong?
My reading of the standard is a little different -
section 25.4 deals with sorting algorithms rather than for sorted containers; the context established in 25.4.(1) means that the property of the compare object specified in 25.4.(2) applies to the algorithms in 25.4, not to sorted containers
1
All the operations in 25.4 have two versions: one that takes a
function object of type Compare and one that uses an operator.
2
Compare is a function object type (20.8). The return value of the
function call operation applied to an object of type Compare, when
contextually converted to bool (4), yields true if the first argument
of the call is less than the second, and false otherwise. Compare comp
is used throughout for algorithms assuming an ordering relation. It is
assumed that comp will not apply any non-constant function through the
dereferenced iterator.
I don't know whether your example should work or not, but I don't think section 25.4 is applicable here.
A quick test with a vector and std::sort works:
#include <list>
#include <algorithm>
struct YesNo { // Return value type of Comperator
int val_;
explicit YesNo(int y) : val_{y} {}
explicit operator bool() { return val_!=0; }
};
static const YesNo yes{1};
static const YesNo no{0};
struct LessYesNo { // Comperator with special return values
YesNo operator()(int a, int b) const {
return a<b ? yes : no;
}
};
int main() {
std::vector<int> data {2,3,4,1,2};
std::sort(std::begin(data), std::end(data), LessYesNo());
}
Edit:
The associative container's Compare parameter is defined in terms of secion 25.4:
1 Associative containers provide fast retrieval of data based on keys. The library provides four basic kinds of associative containers: set, multiset, map and multimap.
2 Each associative container is parameterized on Key and an ordering relation Compare that
induces a strict weak ordering (25.4) on elements of Key. In addition, map and multimap associate an arbitrary type T with the Key. The object of type Compare is called the comparison object of a container.
and 23. has no other conditions on the type of Compare as far as I can see, so it does seem reasonable to assume that a type satisfying the constraints of 25.4 are equally applicable.
Did I understand the Std correctly that for set also explicit bool conversions should work?
This is sort of a grey area of the specification. The return value from the comparison function is required to be "convertible to bool". But what that means in light of explicit operator bool() is unclear.
For example, one could write std::set's comparison usage as this:
CompFunc functor;
if(functor(input, currVal))
...
Or, one could do this:
CompFunc functor;
bool test = functor(input, currVal);
if(test)
...
Are both of these technically legal under C++11? No idea. Obviously the second one fails if operator bool() is explicit.
I looked at the definition of std::shared_ptr, and it has an explicit operator bool() as well. It also says that std::shared_ptr is "convertible to bool", in section 20.7.2.2, paragraph 2.
So I'm guessing that the second version should be implemented as follows:
CompFunc functor;
bool test = static_cast<bool>(functor(input, currVal));
if(test)
...
The fact that it's not explicitly stated anywhere in the spec means that it should be filed as a defect report. But it should probably also be filed as a GCC/libstdc++ bug.
Personally, to be safe, I wouldn't rely on it.
On Contextual Conversion
Section 4, paragraph 3 states:
An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t
So operations which are "contextually convertible to bool" means that explicit operator bool() will work. Since std::set's "Compare" functor must fall under the requirements of 25.4, and these requirements include "contextually converted to bool", it looks like a GCC/libstdc++ bug.
I'd still avoid doing it when you can help it, though.
I am confused about the interface of std::find. Why doesn't it take a Compare object that tells it how to compare two objects?
If I could pass a Compare object I could make the following code work, where I would like to compare by value, instead of just comparing the pointer values directly:
typedef std::vector<std::string*> Vec;
Vec vec;
std::string* s1 = new std::string("foo");
std::string* s2 = new std::string("foo");
vec.push_back(s1);
Vec::const_iterator found = std::find(vec.begin(), vec.end(), s2);
// not found, obviously, because I can't tell it to compare by value
delete s1;
delete s2;
Is the following the recommended way to do it?
template<class T>
struct MyEqualsByVal {
const T& x_;
MyEqualsByVal(const T& x) : x_(x) {}
bool operator()(const T& y) const {
return *x_ == *y;
}
};
// ...
vec.push_back(s1);
Vec::const_iterator found =
std::find_if(vec.begin(), vec.end(),
MyEqualsByVal<std::string*>(s2)); // OK, will find "foo"
find can't be overloaded to take a unary predicate instead of a value, because it's an unconstrained template parameter. So if you called find(first, last, my_predicate), there would be a potential ambiguity whether you want the predicate to be evaluated on each member of the range, or whether you want to find a member of the range that's equal to the predicate itself (it could be a range of predicates, for all the designers of the standard libraries know or care, or the value_type of the iterator could be convertible both to the predicate type, and to its argument_type). Hence the need for find_if to go under a separate name.
find could have been overloaded to take an optional binary predicate, in addition to the value searched for. But capturing values in functors, as you've done, is such a standard technique that I don't think it would be a massive gain: it's certainly never necessary since you can always achieve the same result with find_if.
If you got the find you wanted, you'd still have to write a functor (or use boost), since <functional> doesn't contain anything to dereference a pointer. Your functor would be a little simpler as a binary predicate, though, or you could use a function pointer, so it'd be a modest gain. So I don't know why this isn't provided. Given the copy_if fiasco I'm not sure there's much value in assuming there are always good reasons for algorithms that aren't available :-)
Since your T is a pointer, you may as well store a copy of the pointer in the function object.
Other than that, that is how it is done and there's not a whole lot more to it.
As an aside, it's not a good idea to store bare pointers in a container, unless you are extremely careful with ensuring exception safety, which is almost always more hassle than it's worth.
That's exactly what find_if is for - it takes a predicate that is called to compare elements.
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.