In the examlpe below I need to define a function to compare my objects using certain rules in getHappiness(Animal*) method. The method cannot be static and rather complicated. I need a pointer in the comparison definition to call getHappiness method.
So my question is: how do I pass a pointer to this method, it gets called automatically when I insert an element into the map. And also it doesn't seem that I can instantiate Compare structure and pass the pointer to the constructor.
Am I doing anything wrong? Maybe there is an alternative way to how I define a comparison function?
struct Compare {bool operator()(Animal* const, Animal* const) const;};
bool
Compare::operator()(Animal* const a1, Animal* const a2) const {
Zoo* zoo; // somehow I need to get access to the Zoo instance here
if (zoo->getHappiness(a1) > zoo->getHappiness(a2)) return true;
return false;
}
Class Zoo(){
std::multimap<Animal*, Man*, Compare> map;
int getHappiness(Animal*); // cannot be static
}
int main(){
...
Zoo zoo;
zoo.map.insert(...);
...
}
There is a design issue in your code. Happiness should be an attribute which belong to an animal not a zoo. So implement getHappiness() on animal makes your code much simpler:
struct Compare
{
bool operator()(Animal& const, Animal& const) const;
};
bool Compare::operator()(Animal& const a1, Animal& const a2) const
{
return a1.getHappiness() < a2.getHappiness();
}
Class Zoo(){
std::multimap<Animal, Man, Compare> map;
}
Also, if not necessary, don't use pointer. If you can't avoid pointer, use smart pointer in STL container.
Related
If I have the following:
class Animal {};
class Penguin : public Animal {};
class Snake : public Animal {};
class Zoo
{
std::vector<std::shared_ptr<Animal>> animals;
public:
const std::vector<std::shared_ptr<Animal>>& GetAnimals() { return animals; }
std::shared_ptr<Penguin> AddPenguin()
{
auto result = std::make_shared<Penguin>();
animals.push_back(result);
return result;
}
std::shared_ptr<Snake> AddSnake()
{
auto result = std::make_shared<Snake>();
animals.push_back(result);
return result;
}
};
I'd like to keep const correctness, and be able to add the following method:
const std::vector<std::shared_ptr<const Animal>>& GetAnimals() const
{
return animals;
}
However, that doesn't compile because the return type doesn't match animals. As the const is embedded deep in the type, const_cast isn't able to convert.
However, this compiles and appears to behave:
const std::vector<std::shared_ptr<const Animal>>& GetAnimals() const
{
return reinterpret_cast<const std::vector<std::shared_ptr<const Animal>>&>(animals);
}
I realize reinterpret_casts can be dangerous, but are there any dangers to use it in this scenario? Do the types being cast between have the same memory layout? Is the only effect of this to prevent the caller from then calling any non-const methods of Animal?
Update
I've realized this isn't entirely const correct. The caller could call .reset() on one of the elements of the vector, which would still modify the zoo. Even so, I'm still intrigued what the answer is.
Update on the update
I got that wrong, the code I was trying accidentally copied the shared_ptr and so the shared_ptr in the vector can't be reset when the vector is const.
std::shared_ptr<Animal> and std::shared_ptr<const Animal> are fundamentally different types. Messing with reinterpret_cast can lead to very strange bugs down the road (mostly due to optimizations, I would imagine). You have two options: create a new std::shared_ptr<const Animal> for each std::shared_ptr<Animal>, or return a complex proxy type (something like a view of the vector).
That said, I question the need for GetAnimals. If Zoo is meant to be a collection of pointers to animals, can't you provide access functions like size, operator[], and perhaps iterators? This does involve more effort, but if all you want is a function that returns the whole vector, why have a Zoo class in the first place? If Zoo contains other data and manages more than just a vector of animals, then I would make a separate class to take care of that part, AnimalList or something. That class can then provide appropriate access functions.
Something else you might try is to keep a std::shared_ptr<std::vector<Animal>> instead that you can easily convert into a std::shared_ptr<const std::vector<Animal>>. That may or may not be relevant depending on the reason you need shared pointers.
You can probably solve your problem with std::experimental::propagate_const. It is a wrapper for pointer-like types that properly propagates const-correctness.
A const std::shared_ptr<Animal> holds a mutable Animal. Retrieving a mutable reference to the mutable animal is legal, because the pointer itself is not changed. Vice versa, a std::shared_ptr<Animal const> will always hold a const Animal. You would have to explicitly cast away constness to mutate the held element which is ugly to say the least. Dereferencing a std::experimental::propagate_const<std::shared_ptr<Animal>> on the other hand returns a Animal const& if it is const, and a Animal& if it is not const.
If you wrap your shared pointers in std::experimental::propagate_const you can equip Zoo with a const and a non-const getter for your animals vector and have const-correctness (or you could make animals a public data member, since the getters don't do anything special. This would make your API more transparent):
#include <vector>
#include <memory>
#include <experimental/propagate_const>
#include <type_traits>
class Animal {};
class Penguin : public Animal {};
class Snake : public Animal {};
class Zoo
{
template <typename T>
using pointer_t = std::experimental::propagate_const<std::shared_ptr<T>>;
std::vector<pointer_t<Animal>> animals;
public:
// const-getter
auto const& GetAnimals() const
{
return animals;
}
// non-const getter
auto& GetAnimals()
{
return animals;
}
std::shared_ptr<Penguin> AddPenguin()
{
auto result = std::make_shared<Penguin>();
animals.push_back(result);
return result;
}
std::shared_ptr<Snake> AddSnake()
{
auto result = std::make_shared<Snake>();
animals.push_back(result);
return result;
}
};
int main() {
Zoo zoo;
zoo.AddSnake();
// non-const getter will propagate mutability through the pointer
{
auto& test = zoo.GetAnimals()[0];
static_assert(std::is_same<Animal&, decltype(*test)>::value);
}
// const-getter will propagate const through the pointer
{
Zoo const& zc = zoo;
auto& test = zc.GetAnimals()[0];
static_assert(std::is_same<Animal const&, decltype(*test)>::value);
}
return 0;
}
https://godbolt.org/z/1rd8YraMc
The only downsides I can think of are the discouraging "experimental" namespace and the fact that afaik MSVC hasn't implemented it yet, so it is not as portable as it could be....
If that bothers you, you can write your own propagate_const wrapper type, as #Useless suggested:
template <typename Ptr>
class propagate_const
{
public:
using value_type = typename std::remove_reference<decltype(*Ptr{})>::type;
template <
typename T,
typename = std::enable_if_t<std::is_convertible<T, Ptr>::value>
>
constexpr propagate_const(T&& p) : ptr{std::forward<T>(p)} {}
constexpr value_type& operator*() { return *ptr; }
constexpr value_type const& operator*() const { return *ptr; }
constexpr value_type& operator->() { return *ptr; }
constexpr value_type const& operator->() const { return *ptr; }
private:
Ptr ptr;
};
https://godbolt.org/z/eGPPPxef4
I am trying to solve this problem. I came up with this solution:
typedef unordered_map<string, double> stockDictType;
class StockTicker {
class Comparator {
public:
inline bool operator() (const string &a, const string &b) const {
return stocksDict.at(a) < stocksDict.at(b);
}
};
stockDictType stocksDict;
map<string, stockDictType::iterator, Comparator> stocksTicker; // this is where I need a custom comparator method
int tickerSize;
public:
StockTicker(int k): tickerSize(k) {}
// some other methods
};
As is evident, this fails to compile: StockTicker::stocksDict is not a static member. Now I can't make it so because I could require multiple instances of the StockTicker class.
std::map uses a strict comparator function parameter definition (std::map will only pass in the keys to be compared), so I can't overload it to pass a reference to the current instance of the StockTicker class (which I could've used to get access to StockTicker::stocksDict through public getters)
I took inspiration from this SO question and the subsequent answer to do this:
typedef unordered_map<string, double> stockDictType;
class StockTicker {
class Comparator {
public:
stockDictType &_stockDictRef;
explicit Comparator(stockDictType &stocksDict): _stockDictRef(stocksDict) {}
inline bool operator() (const string &a, const string &b) const {
return _stockDictRef.at(a) < _stockDictRef.at(b);
}
};
stockDictType stocksDict;
map<string, stockDictType::iterator, Comparator> stocksTicker(Comparator{stocksDict});
int tickerSize;
public:
StockTicker(int k): tickerSize(k) {}
void addOrUpdate(string name, double price) {
stocksDict[name] = price;
stocksTicker.at(name) = stocksDict.find(name);
}
vector<stockDictType::iterator> top() {
vector<stockDictType::iterator> ret(tickerSize);
auto it = stocksTicker.begin();
for(int i = 0; i < tickerSize; i++, it++)
ret[i] = it->second;
return ret;
}
};
This won't compile either. I get this kind of error in the StockTicker::addOrUpdate() and StockTicker::top() methods:
error: '((StockTicker*)this)->StockTicker::stocksTicker' does not have class type.
I tried a bunch of other stuff too (like declaring a public comparator method in the StockTicker class itself and trying to pass a function pointer of it to std::map. That failed as well; StockTicker::stocksTicker gets declared before the comparator method does and the compiler complains).
Any ideas on how to go about fixing this?
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker(Comparator(stocksDict));
this defines a member function named stocksTicker that takes a stocksDict argument of type Comparator and returns a std::map.
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker{Comparator{stocksDict}};
This defines a member variable stocksTicker which, by default, is initialized with a Comparator, which in turn was initialized with the member variable stocksDict.
I assume you want the second.
Your syntax was partway between the two. Whatever compiler you had got confused by this.
Live example
You should StockTicker(StockTicker &&)=delete and StockTicker& operator=(StockTicker &&)=delete, as maps containing references to their containing class are not safe to move or copy.
Generating an efficient move here is tricky. I suspect C++17 node splicing might make it possible to do it. You might have to embed a std::shared_ptr<stocksDict*> (yes, a shared pointer to a pointer), and use .key_comp to reseat the stocksDict in the target.
I'm trying to understand better how does const-correctness work and more specifically, when dealing with classes whose members are based on containers and smart pointers.
I guess that the const-correctness property is the same regardless of the class members. However, since I'm having some difficulties to clearly understand what's going on,
I decided to ask you for advice.
So, here is the context. I've a ShapeContainer class that has as private class member a vector of smart pointers.
The Shape class is abstract and has the following virtual function virtual float doSomething(); which is then redefined by its derived classes. Note that it's a non-const class function.
The relevant part of the code is given below:
class ShapeContainer{
public:
typedef std::shared_ptr<Shape> ShapePtr;
typedef std::vector<ShapePtr> ShapePtrContainer;
// .......
const ShapePtr & operator[]( int ) const { return m_vect[index]; }; // const version
// ShapePtr & operator[]( int ) { return m_vect[index]; }; // non-const version
// .......
private:
ShapePtrContainer m_vect;
};
class Shape{
public:
// ...
virtual float doSomething() = 0;
};
Here are my questions.
Q1. Why do I'm allowed to call the doSomething() function in the following way: int index = 0; float tmp = container1[index]->doSomething(); (having ShapeContainer container1=createBasicShapes();)?
From what I understand, after calling to the const ShapePtr operator[] const function we'll get a const pointer to a Shape object, however the doSomething() virtual
function is not const. So, how does a reference to a const-object can call a non-const function?
Q2. By calling the doSomething() function as previouly ilustrated (float tmp =container1[index]->doSomething();) and by adding a non-const version of the operator[], this latter
overloaded version is then called instead of the const-version one. Why does it is so?
Now, instead of having a ShapeContainer class, I've now a new class named ShapeContainerInfo that still has a vector but of an intermediate ShapeInfo class (that has a smart pointer as a class member).
class ShapeContainerInfo{
public:
typedef std::vector<ShapeInfo> ShapeContainer;
const ShapeInfo & operator []( int index) const { return m_vect[index]; };
// ShapeInfo & operator []( int index) { return m_vect[index]; }; // non-const version
private:
ShapeContainer m_vect;
};
class ShapeInfo{
public:
typedef std::shared_ptr<Shape> ShapePtr;
// ...
float doSomething(){ return m_ShapePtr->doSomething(); };
private:
ShapePtr m_ShapePtr;
int m_nID;
};
Q3. When I call float tmp = container2[i].doSomething();, I get the following compiler error: error C2662: 'ShapeInfo::doSomething' : cannot convert 'this' pointer from 'const ShapeInfo' to 'ShapeInfo &'.
However, when I add a non-const vesion of the overloaded operator [] the compiler error is gone. So, why do I really need the non-const operator[] for ShapeContainerInfo and not for ShapeContainer?
Q4. If the m_vect private member of ShapeContainerInfo is set now as public member and only the const-version of operator[] is defined (not the non-const one), there are no compiler error messages. Why this? e.g. after setting m_vect to be a public class member: float tmp = info.m_vect[i].doSomething();
Q5. How could I correctly define both the ShapeInfo and ShapeContainerInfo classes such that I only need to define the const-version of the operator[] and still being able to call the float doSomething() function?
For those of you interested in the whole sample code, please find it here.
Clarifications, suggestions are always welcomed :-)
Merci!
Q1: The shared_ptr is const, that doesn't mean that the pointed to object is const. For that you would want shared_ptr<const Shape>.
Q2: Since you're ShapeContainer was not const, the non-const function was a better match, so it was called instead of the const version.
Q3: vector propagates its constness to its elements. shared_ptr does not. This is inline with the behavior of arrays and raw pointers. The elements of const arrays are const. The thing pointed to by a const pointer is not (necessarily) const.
Q4: Are you saying this produces no error?
ShapeContainerInfo info;
info[0].doSomething();
Please clarify, because that should be an error.
Q4: Okay, so you're saying that this produces no error:
ShapeContainerInfo info;
info.m_vect[0].doSomething();
Nor should it. The vector is not const. It's only inside the const member function that the vector(and all other members) are treated as const.
Q5: Make m_vect a vector of unique pointers. Inside the const function, the vector itself will be const, and the unique pointers will be const. But the objects that the unique pointers point to will be mutable.
As an example, the set function in this class is not legal:
struct Foo
{
void set(int index, int value) const
{
v[index] = value;
}
std::vector<int> v;
};
But this one is:
struct Foo
{
void set(int index, int value) const
{
*v[index] = value;
}
std::vector<std::unique_ptr<int>> v;
};
I have two classes with inheritance. I want to allow the classes to store their function in one map. How can I create the find function by the key? And will the inheritance of the function work (MyClass_2 does not have its own doSmth() function, it should add the doSmth() from MyClass)? My attempt is below.:
template<class T>
class Class1
{
typedef void(T::*MyFunction)();
map<string, MyFunction> functionMap;
public:
void addFunc(string funcName, MyFunction function) {
functionMap.insert(pair<string, MyFunction>(funcName, function));
}
bool findFunc(string key) {
typename map<string, MyFunction>::iterator it;
it = functionMap.find(key.c_str());
}
};
class MyClass {
Class1<MyClass> class1;
void addFunc() {
class1.addFunc("Func1", &MyClass::doSmth);
}
void findAndCallFunc(string key) {
class1.findFunc(key);
}
void doSmth();
};
class MyClass_2: MyClass {
Class1<MyClass_2> class1;
void addFunc() {
class1.addFunc("Func1", &MyClass_2::doSmth);
}
}
EDIT: I tested my programm. It works. My problem is that if I call fundAndCallFunc from an object of MyClass_2. It takes not the class1 of MyClass_2, but class1 of MyClass.
What should I change?
As Chris says, your findFunc should return the actual function:
MyFunction findFunc(string key)
{
const map<string, MyFunction>::const_iterator it = functionMap.find(key);
return it == functionMap.end() ? NULL : it->second;
}
Further, if you only store the function pointer to a member function, you lose track of the actual object (so you could just make your map static!). Perhaps you should also store the this pointer of the object, i.e. make your map like this:
std::map<std::string, std::pair<T*, MyFunction> > functionMap;
In addFunc you would say
functionMap.insert(std::make_pair(funcName, std::make_pair(this, function)));
Usage: Suppose it = functionMap.find("key"). Then you could say:
MyClass * const p = it->second.first;
MyFunction f = it->second.second;
p->*f(); // invoke -- yes, it's operator->*() in action
You have two immediately obvious problems here:
your findFunc method just looks up the function, it doesn't do anything with it (attempt to call it or return the method pointer), and its declared as returning bool rather than MyFunction -- not sure what you want it to do
your doSmth method is private in MyClass, so you can't access it in MyClass_2. You'll need to make it protected or public.
Edit1: I realize this is hard to understand this question without having an insight of what I'm trying to do. The class A is not complete but it essentially stand for a C-array "proxy" (or "viewer" or "sampler"). One interesting usage is too present a C-array as a 2d grid (the relevant function are not shown here). The property of this class are the following:
it should not own the data - no deep copyy
it should be copyable/assignable
it should be lightweight (
it should preserve constness (I'm having trouble with this one)
Please do not question the purpose or the design: they are the hypothesis of the question.
First some code:
class A
{
private:
float* m_pt;
public:
A(float* pt)
:m_pt(pt)
{}
const float* get() const
{
return m_pt;
}
void set(float pt)
{
*m_pt = pt;
}
};
void gfi()
{
float value = 1.0f;
const A ac(&value);
std::cout<<(*ac.get())<<std::endl;
A a = ac;
a.set(2.0f);
std::cout<<(*ac.get())<<std::endl;
}
Calling "gfi" generate the following output:
1
2
Assigning a with ac is a cheap way to shortcut the constness of ac.
Is there a better way to protect the value which m_pt point at?
Note that I DO want my class to be copyable/assignable, I just don't want it to loose its constness in the process.
Edit0: I also DO want to have a pointer in there, and no deep copy please (let say the pointer can be a gigantic array).
Edit2: thanks to the answers, I came to the conclusion that a "const constructor" would be a useful thing to have (at least in this context). I looked it up and of course I'm not the same one who reached this conclusion. Here's an interesting discussion:
http://www.rhinocerus.net/forum/language-c-moderated/569757-const-constructor.html
Edit3: Finally got something which I'm happy with. Thanks for your help. Further feedback is more than welcome
template<typename T>
class proxy
{
public:
typedef T elem_t;
typedef typename boost::remove_const<T>::type elem_unconst_t;
typedef typename boost::add_const<T>::type elem_const_t;
public:
elem_t* m_data;
public:
proxy(elem_t* data = 0)
:m_data(data)
{}
operator proxy<elem_const_t>()
{
return proxy<elem_const_t>(m_data);
}
}; // end of class proxy
void test()
{
int i = 3;
proxy<int> a(&i);
proxy<int> b(&i);
proxy<const int> ac(&i);
proxy<const int> bc(&i);
proxy<const int> cc = a;
a=b;
ac=bc;
ac=a;
//a=ac; // error C2679: binary '=' : no operator found which takes a right-hand operand of type...
//ac.m_data[0]=2; // error C3892: 'ac' : you cannot assign to a variable that is const
a.m_data[0]=2;
}
Your class is badly designed:
it should use float values, not pointers
if you want to use pointers, you probably need to allocate them dynamically
and then you need to give your class a copy constructor and assignment operator (and a destructor) , which will solve the problem
Alternatively, you should prevent copying and assignment by making the copy constructor and assignment op private and then not implementing them.
You can trick around with proxy pattern and additional run-time constness boolean member. But first, please tell us why.
Effectively your class is like an iterator that can only see one value. It does not encapsulate your data just points to it.
The problem you are facing has been solved for iterators you should read some documentation on creating your own iterator and const_iterator pairs to see how to do this.
Note: in general a const iterator is an iterator that cannot be incremented/decremented but can change the value it points to. Where as a const_iterator is a different class that can be incremented/decremented but the value it points to cannot be changed.
This is the same as the difference between const float * and float *const. In your case A is the same as float * and const A is the same as float *const.
To me your choices seem to be:
Encapsulate your data.
Create a separate const_A class like iterators do
Create your own copy constructor that does not allow copies of const A eg with a signature of A(A & a);
EDIT: considering this question some more, I think you are misinterpreting the effect of const-correctness on member pointers. Consider the following surprising example:
//--------------------------------------------------------------------------------
class CNotSoConstPointer
{
float *mp_value;
public:
CNotSoConstPointer(float *ip_value) : mp_value(ip_value) {}
void ModifyWithConst(float i_value) const
{
mp_value[0] = i_value;
}
float GetValue() const
{
return mp_value[0];
}
};
//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
float value = 12;
const CNotSoConstPointer b(&value);
std::cout << b.GetValue() << std::endl;
b.ModifyWithConst(15);
std::cout << b.GetValue() << std::endl;
while(!_kbhit()) {}
return 0;
}
This will output 12 and then 15, without ever being "clever" about the const-correctness of the const not-so-const object. The reason is that only the pointer ITSELF is const, not the memory it points to.
If the latter is what you want, you'll need a lot more wrapping to get the behavior you want, like in my original suggestion below or Iain suggestion.
ORIGINAL ANSWER:
You could create a template for your array-proxy, specialized on const-arrays for the const version. The specialized version would have a const *m_pt, return a const pointer, throw an error when you try to set, and so on.
Edit: Something like this:
template<typename T>
class TProxy
{
T m_value;
public:
TProxy(T i_t) : m_value(i_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.m_value) {}
T get() { return m_value; }
void set(T i_t) { m_value = i_t; }
};
template<typename T>
class TProxy<const T *>
{
const T *mp_value;
public:
TProxy(const T *ip_t) : mp_value(ip_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.mp_value) {}
T get() { return m_value; }
};
Why not replace float* with float in A. If you don't either the original owner of the float that the float* references can change it, or anyone prepared to do a mutable cast on the return value from a::get.
const is always just a hint to the compiler; there are no ways to make a variable permanently read-only.
I think you should use deep copy and define your own assingment operator and copy constructor.
Also to return handle to internal data structure in not a good practice.
You can deny the copy-constructor for certain combinations of arguments:
For instance, adding the constructor;
A(A& a) :m_pt(a.m_pt) { m_pt = a.m_pt; }
prevents any instance of A being initialised with a const A.
This also prevents const A a2 = a1 where a1 is const, but you should never need to do this anyway, since you can just use a1 directly - it's const even if you could make a copy, a2 would be forever identical to a1.