This question already has answers here:
cast const Class using dynamic_cast
(2 answers)
Closed 9 years ago.
I have the following classes and methods:
//Base class
class Node {
public:
virtual ~Node() {};
Node() {};
private:
// Private things for my implementation.
};
class Element : public Node {
public:
// Returns the name of the element.
const xml::String &name() const {
return eleName;
}
static bool is_Element(const Node *base) {
Element *p = NULL;
p = dynamic_cast<Element*>(base);
return (p!=NULL);
}
static const Element *to_Element(const Node *base) {
return dynamic_cast<Element*>(base);
}
private:
s_namespace eleNamespace;
xml::String &eleName;
// Private things for my implementation.
};
Here when I dynamic cast it says the following compile error. How to correct it? One method is to simple remove the const of the parameters. But I donot think that is the correct method.
oops.cpp: In static member function ‘static bool
xml::Element::is_Element(const xml::Node*)’: oops.cpp:208:44: error:
cannot dynamic_cast ‘base’ (of type ‘const class xml::Node*’) to type
‘class xml::Element*’ (conversion casts away constness) oops.cpp: In
static member function ‘static const xml::Element*
xml::Element::to_Element(const xml::Node*)’: oops.cpp:213:47: error:
cannot dynamic_cast ‘base’ (of type ‘const class xml::Node*’) to type
‘class xml::Element*’ (conversion casts away constness)
Use dynamic_cast<const Element*> instead.
You can also make your class const-correct, by implementing two different functions for a const-Argument and a non-const Argument:
static const Element *to_Element(const Node *base) {
return dynamic_cast<const Element*>(base);
}
static Element *to_Element(Node *base) {
return dynamic_cast<Element*>(base);
}
So if the caller has a non-const Node he probably also wants a non-const Element and now he can get it...
Try this:
static bool is_Element(const Node *base) {
const Element *p = NULL; // Add a const keyword here
p = dynamic_cast<const Element*>(base); // Add a const keyword here
return (base!=NULL);
}
And the same for to_Element
Related
So this is my first question. I've searched the site, found something and applied the suggestions given in them but I'm still unsure if I've done it the right way.
I'm working on a template library and here's my implementation of BST class template:
template <class T>
class bstree
{
private:
struct bstnode
{
bstnode* pRight; //node to the right (greater)
bstnode* pLeft; //node to the left (lesser)
bstnode* pParent; //parent node
T mValue; //contents
};
class bstnodeiterator : public _iterator_base<T, bstree<T>>
{
public:
bstnodeiterator(bstnode* pNode = nullptr, bstree<T> pCont = nullptr)
: _mpNodePtr(pNode), _mpCont(pCont) {}
//functions from _iterator_base<>
bool is_null() const { return (_mpNodePtr == nullptr); }
const bstree<T>* get_container() const { return this->_mpCont; }
//get_pointer() is intentionally not defined.
//operators (e.g. increment, decrement, advance by, dereference, etc)
//go here!
//...
private:
friend class bstree<T>;
//member elements:
bstree<T>* _mpCont; //the container that the iterator is created by
bstnode* _mpNodePtr; //the actual pointer pointing to the bst-node of '_mpCont'
};
public:
using val = T;
using val_ref = T&;
using val_ptr = T*;
using iter = bstnodeiterator;
public:
iter begin() const;
iter end() const;
//other public member functions (e.g. insert(), remove(), etc.) go here!
//...
private:
bstnode* _mpRoot; //The root node of the BST
size_t _mSize; //The number of elements in the container (guaranteed O(1))
};
bstnodeiterator::get_container() and bstnodeiterator::is_null() are derived from iterator_base<> which is a base class of iterators for all the other containers (e.g. vector<>, fixed_list<>, map<>, etc):
template <class T, class Cont>
struct _iterator_base
{
virtual bool is_null() const = 0;
virtual const Cont* get_container() const = 0;
/*virtual*/ const T* get_pointer() const /* = 0*/;
};
//is_null() and get_container() should be defined in derived classes
//because they are used everywhere in the library!
All three functions above are needed to be defined because they are used in everywhere else in the entire library (e.g. in algorithms, in iterator_helper class, etc).
Since a BST is a container of sorted elements, the contents of a node should not be changed dynamically. Because this will break the sorted structure of the tree. Thus, I want to prevent the programmer to use get_pointer(). Even if it returns a const pointer to the contents it can still be changed via member functions of T (For example, if T is a std::string then the contents can be changed via std::string::assign()) and I don't want this.
So, I made the function _iterator_base<*,*>::get_pointer() non-virtual in the base class. And it's not defined in the derived class, bstnodeiterator. So, if the programmer calls it from the derived class ...
bstree<std::string> strTree = { "a string", "another string", "yet another string", "test string" };
//inserted some other elements
bstree<std::string>::iterator it = strTree.begin();
//*it = "something else"; --> this won't work, because read-only dereferencing is allowed in the class.
it.get_pointer()->assign("something else"); //this will break the tree.
... then the compiler will give a linkage error: unresolved external symbol " ... ::get_pointer()".
Is this the correct way? What do you think?
EDIT:
I've just tried dereferencing and modifying:
bstree<std::string> strTree =
{
"a string",
"another string",
"yet another string",
"test string"
};
bstree<std::string>::iter it = strTree.begin();
(*it).assign("modified string"); // ----> error!
std::string pB0 = strTree.begin(); // ----> error
const std::string pB = strTree.begin();
pB->assign("modified string"); // ----> error!
...and it didn't compile. But if I change the last line with following:
it.get_pointer()->assign("modified string");
... it compiles without errors, runs, and works!
EDIT 2:
I've finally found the root of the problem: typedefs.
I didn't show the typedefs in the original question to make it look simpler and easier to read. In the original code, there is a using val_ptr = T*; under the scope of bstree<> and I'm using this typedef under the scope of bstnodeiterator:
template <class T>
class bstree
{
public:
using val = T;
using val_ref = T&;
using val_ptr = T*;
private:
class bstnodeiterator : public _iterator_base<T, bstree<T>>
{
//c'tor comes here!
const val_ptr get_pointer() { return (_mPtr ? &_mPtr->_mVal : nullptr); }
//...
};
//...
};
If I define the function as given above then I can call std::string::assign() from the returning pointer of get_pointer(). However, if I change the function's return type to const val* then I can't call string::assign().
I've finally realized that these two types are different. Probably the compiler puts the const somewhere else.
In response to OP's second edit:
Aliases are not like macros.
If you write using PtrType = T*, then const PtrType is actually
equivalent to T* const, which is a const pointer to a T object, not a pointer to a const T object. When aliases are used, further cv-qualifiers are always added on the top level. It is intuitive - if PtrType is a pointer to T, then const PtrType should be a const pointer to T.
As per the question, if you don't want users to call a virtual function, make it protected, so derived classes can implement it, but outside users cannot call it.
It is likely that you made the return type of bstnodeiterator::get_pointer() T* (instead of const T*).
You may be experiencing the pitfall of c++ covariant return types.
Both types are pointers or references (lvalue or rvalue) to classes. Multi-level pointers or references are not allowed.
The referenced/pointed-to class in the return type of Base::f() must be a unambiguous and accessible direct or indirect base class of (or is the same as) the
referenced/pointed-to class of the return type of Derived::f().
The return type of Derived::f() must be equally or less cv-qualified than the return type of Base::f().
Note: c++ reference does not have the "(or is the same as)" clause, but it is added to be coherent with the standard"
So std::string* is a valid return type if the function is overriding a function with return type const std::string*.
Consider this example:
#include <string>
std::string s = "Hello, world";
struct Base {
virtual const std::string* foo() = 0;
};
struct Derived : Base {
std::string* foo() override {
return &s;
}
};
int main() {
Derived d;
d.foo()->assign("You can do this.");
return 0;
}
The above code compiles: you can modify the string pointed by d.foo() because it returns a std::string*.
I am pretty new to C++ and have a problem regarding pointers/references. The following example reflects my problem:
#include <iostream>
#include "boost/make_shared.hpp"
#include "boost/utility.hpp"
class UsedObjectInterface {
public:
virtual int data() const = 0;
};
class UsedObject : public UsedObjectInterface {
public:
UsedObject() : data_(42) {
}
explicit UsedObject(int value) : data_(value) {
}
int data() const {
return data_;
}
private:
const int data_;
};
class BaseClient : private boost::noncopyable {
public:
virtual const UsedObjectInterface& used_object() const = 0;
};
class SegmentationFaultClient : public BaseClient {
public:
// This can't work, since the object is deleted immediately.
// IMHO only the following two solutions can work:
// 1. The member attribute is not a reference (not possible with an abstract class, we lose the advantages of polymorphism).
// 2. The member attribute is a pointer.
SegmentationFaultClient() : used_object_(UsedObject()) {
}
explicit SegmentationFaultClient(const UsedObjectInterface& used_object)
: used_object_(used_object) {
}
const UsedObjectInterface& used_object() const {
return this->used_object_;
}
private:
const UsedObjectInterface& used_object_;
};
class CorrectClient : public BaseClient {
public:
CorrectClient() : used_object_(boost::make_shared<UsedObject>()) {
}
explicit CorrectClient(const boost::shared_ptr<UsedObjectInterface> used_object)
: used_object_(used_object) {
}
// TODO Is it possible to change this to a const&, so at least the interface
// is the same as in SegmentationFaultClient? Then the above constructor can
// be deleted.
explicit CorrectClient(const UsedObjectInterface& used_object)
: used_object_(&used_object) {
// TODO How-to convert a raw pointer to a smart pointer?
}
const UsedObjectInterface& used_object() const {
return *this->used_object_;
}
private:
const boost::shared_ptr<UsedObjectInterface> used_object_;
};
int main() {
SegmentationFaultClient segfault_client;
const UsedObjectInterface& a = segfault_client.used_object();
std::cout << a.data() << std::endl;
// Correct, but how to make this work with a const& constructor?
const UsedObject first_object;
CorrectClient correct_client(first_object);
const UsedObjectInterface& b = correct_client.used_object();
std::cout << b.data() << std::endl;
}
As the comments say, the implementaton of the SegmentationFaultClient class is simply wrong, since the default constructor creates an object on the stack, which is "immediately" removed. Therefore I came up with the class CorrectClient, which uses pointers. My goal is to keep the nice API (no public Boost) from SegmentationFaultClient (const& default constructor). The above example does not work and terminates with the following error:
invalid conversion from 'const UsedObjectInterface*' to 'boost::shared_ptr<UsedObjectInterface>::element_type* {aka UsedObjectInterface*}' [-fpermissive]
explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete
So my question is: Is it possible to convert a raw pointer * to a smart pointer? If so, what is the best way to do that? If you see any other problems with my code, please let me know, too!
Problem is, that you are trying to convert const-pointer to a non-const pointer. You can use reference as param, instead of const-reference, or you can do following
const boost::shared_ptr<const UsedObjectInterface> used_object_;
However, default-deleter for shared_ptr will be delete pointer, that is in your case not allocated on heap. You should point empty-deleter, or not use shared_ptr in this case.
A few days ago, I took it upon myself to try and write a basic tree implementation in the same style as the STL containers. Now I am trying to use it in my code, but two things don't seem to work that do work with say a std::vector. Namely, using incomplete types and using abstract types.
How do I fix my tree implementation for it to gain this functionality? I've tried to condense my code a bit to show you mainly the relevant parts.
test.cpp
#include "util/tree.hpp"
#include <vector>
struct IncompleteType;
class AbstractType
{
public:
virtual void do_something() = 0;
};
class Test
{
public:
Test() = default;
private:
tree<IncompleteType> incompleteTree;
std::vector<IncompleteType> incompleteVector;
tree<AbstractType> abstractTree;
std::vector<AbstractType> abstractVector;
};
struct IncompleteType
{
int completed;
};
util/tree.hpp (condensed)
template <class T, class Alloc = std::allocator<T> >
class tree
{
public:
typedef Alloc allocator_type;
typedef typename Alloc::value_type value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef typename Alloc::difference_type difference_type;
typedef typename Alloc::size_type size_type;
class node
{
public:
value_type data;
const std::vector<std::unique_ptr<node> >& get_children() const { return children_; }
node* get_parent() const { return parent_; }
node* get_right() const { return right_; }
bool operator== (const node&) const;
size_t size() const;
bool has_ancestor(const node* n) const { return parent_ != nullptr && (parent_ == n || parent_->has_ancestor(n)); }
friend class tree;
protected:
std::vector<std::unique_ptr<node> > children_;
node* parent_ = nullptr;
node* right_ = nullptr;
node() = default;
node(value_type data) : data(data) {}
};
class iterator
{
// ...
};
class const_iterator
{
// ...
};
tree() = default;
tree(const tree&) = default;
tree& operator= (const tree&) = default;
// iterators begin(), etc ...
// operators ...
// size(), empty(), ...
node* get_root() { return &root_; }
const node* get_root() const { return &root_; }
node* add_new_node(const value_type& val) { return add_new_node_to(&root_, val); }
node* add_new_node_to(node*, const value_type&);
bool prune_node(node*&);
private:
node root_;
};
When compiling with g++ -O3 -Wall -Wextra -pedantic -std=c++11 test.cpp, I get the following output:
In file included from test.cpp:1:0:
util/tree.hpp: In instantiation of ‘class tree<IncompleteType>::node’:
util/tree.hpp:138:7: required from ‘class tree<IncompleteType>’
test.cpp:19:30: required from here
util/tree.hpp:28:14: error: ‘tree<T, Alloc>::node::data’ has incomplete type
value_type data;
^
test.cpp:6:8: error: forward declaration of ‘tree<IncompleteType>::value_type {aka struct IncompleteType}’
struct IncompleteType;
^
In file included from test.cpp:1:0:
util/tree.hpp: In instantiation of ‘class tree<AbstractType>::node’:
util/tree.hpp:138:7: required from ‘class tree<AbstractType>’
test.cpp:21:30: required from here
util/tree.hpp:47:3: error: cannot allocate an object of abstract type ‘AbstractType’
node(value_type data) : data(data) {}
^
test.cpp:8:7: note: because the following virtual functions are pure within ‘AbstractType’:
class AbstractType
^
test.cpp:11:15: note: virtual void AbstractType::do_something()
virtual void do_something() = 0;
^
In file included from test.cpp:1:0:
util/tree.hpp:28:14: error: cannot declare field ‘tree<AbstractType>::node::data’ to be of abstract type ‘AbstractType’
value_type data;
^
test.cpp:8:7: note: since type ‘AbstractType’ has pure virtual functions
class AbstractType
^
My tree has trouble with these types, whereas std::vector does not. I can see that it has to do with how I store the data inside the nodes, but I am drawing a blank when trying to come up with the right way to do it... How do I store things if not of type value_type?
Since the type is incomplete the compiler has no way of determinng it's size. Since the size is needed to store the objects by value the compiler whines about it. If you must deal with incomplete types you will need to use a container of pointers. For instance in Test you can use std::vector<std::unique_ptr<IncompleteType>> or std::vector<IncompleteType*>.
There is another issue in your code. tree and vector fail with AbstractClass because you are trying to store it by value. Since it has pure virtual functions it cannot be instantiated when the Node is created.
You need to use a pointer to incomplete type rather than incomplete type itself, i.e something along the lines of value_type* pData instead of value_type data.
If you do that, compiler will not attempt to instantiate an incomplete type as it's ok with pointers to incomplete (but not with incomplete-by-value).
Here is how they do it in vector.h:
template<class _Ty,
class _Alloc>
class _Vector_val
: public _Container_base
{
//blah blah blah
pointer _Myfirst; // pointer to beginning of array
pointer _Mylast; // pointer to current end of sequence
pointer _Myend; // pointer to end of array
_Alty _Alval; // allocator object for values
}
Note how they use pointers all over the place, never the actual object. The only exception here is the allocator object, but my guess is, it also never instantiates value_type.
Hope this helps.
P.S Great question BTW
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'm stuck on a compiler error and I can't seem to find a solution online (mainly because google can't handle the syntax). This is the one and only error I'm getting (MVS 2005).
error C2664: 'LinkedList<T>::CreateLinkedList' : cannot convert parameter 2
from 'const MemberInfo *' to 'MemberInfo *const *' memberdata.cpp 59
The error points to this piece of code.
ILinkedList*
MemberData::CreateLinkedList()
{
const MemberInfo* mi = this->get(FIRST);
LinkedList<MemberInfo*>::CreateLinkedList(
MemberInfo::CompareByTime,
mi);
return NULL;
}
The relevant pieces of code in this are:
MemberInfo class
// MemberInfo declaration
class
MemberInfo
{
public:
static int
CompareByTime(
const void* mi1,
const void* mi2);
};
// MemberInfo implementation
int
MemberInfo::CompareByTime(
const void* mi1,
const void* mi2)
{
if ( mi1 == NULL || mi2 == NULL )
return 0;
if ( ((MemberInfo*)mi1)->m_Time > ((MemberInfo*)mi2)->m_Time )
return 1;
if ( ((MemberInfo*)mi2)->m_Time > ((MemberInfo*)mi1)->m_Time )
return -1;
return 0;
}
LinkedList class
typedef int (*ComparatorFcn)(const void*, const void*);
template <class T>
class LinkedList
: public ILinkedList
{
private:
protected:
const T*
m_ptValue;
ComparatorFcn
m_pCompFcn;
LinkedList(
const T* ptVal,
ComparatorFcn func);
public:
static ILinkedList*
CreateLinkedList(
ComparatorFcn func,
const T* ptVal)
{
LinkedList<T>* t = new LinkedList<T>(ptVal, func);
return t;
}
virtual
~LinkedList();
LinkedList<T>*
AddLink(
T* pLink);
virtual bool
Remove();
virtual bool
RemoveLink(
ILinkedList* pLink);
};
I'm quite stuck. I don't understand why the compiler thinks that my argument for the function CreateLinkedList is MemberInfo *const *rather than how I declared it as const MemberInfo* (or const T* actually).
Any help ideas?
Thanks!
Your LinkedList<MemberInfo*> should be LinkedList<MemberInfo>.
Notice that the error message mentions MemberInfo *const * - a pointer to a pointer.
As the type you use to instantiate the template is a MemberInfo *, T will be MemberInfo * and the CreateLinkedList functions expects a T const * aka a MemberInfo * const *.
The type you pass is a MemberInfo const * aka const MemberInfo *.
So, you're asking the compiler to convert from const MemberInfo * to MemberInfo * const *
I see a couple problems:
First, your MemberInfo::CompareByTime() function is written wrong. The way you have written it throws away any type checking the compiler can do. Better would be:
int MemberInfo::CompareByTime(const MemberInfo& mi1, const MemberInfo& mi2)
{
if(mi1.m_Time > mi2.m_Time)
return 1;
if(mi1.m_Time < mi2.m_Time)
return -1;
return 0;
}
Second, pass the comparison function into your linked list as a template parameter:
template <class T, int (*CompFcn)(const T&, const T&)>
class LinkedList: public ILinkedList
Third, there's no reason to hide the constructor and wrap it in a static function that returns the superclass' pointer. C++ will automatically convert an object's pointer to a pointer to its superclass when needed. Also, you should be passing the contained values around by reference (instead of pointer) and store them by value when possible; if you want your container to store pointers, then just set T to a pointer type. So your construction simplifies to:
protected:
T m_ptValue;
public:
LinkedList(const T& ptVal);
Finally, your code for MemberData::CreateLinkedList is broken. It always returns NULL. That is, from the outside it looks like it never creates a linked list. Also, the this-> does nothing. What you should have is:
LinkedList<MemberInfo*, MemberInfo::CompareByTime>* MemberData::CreateLinkedList()
{
return new LinkedList<MemberInfo*, MemberInfo::CompareByTime>(get(FIRST));
}
Though it's probably good practice to define typedef LinkedList<MemberInfo*, MemberInfo::CompareByTime> LinkedListType; in MemberData, which lets us write:
MemberData::LinkedListType* MemberData::CreateLinkedList()
{
return new LinkedListType(get(FIRST));
}
Note that the return value will be typecast to ILinkedList* automatically where needed.
First of all, there is no difference between const T and T const. It is allowed to add the const keyword after the type. In many situations that is the only way to exactly specify what part of the type const is.
You are using const T* (or T const *). where T is MemberInfo*:
LinkedList<MemberInfo*>::CreateLinkedList(
So the full type is MemberInfo * const *. Note that this is not the same as const MemberInfo * * (or MemberInfo const * *).