I have a class that can take both a non-const pointer or a const pointer as arguments to its overloaded constructors. In my particular case, I need to instantiate an object of this class from both const and non-const methods of class T, but it fails from const methods, as it can't assign the const pointer to foo .
myClass() {
public:
myClass(T* v);
myClass(const T* v);
// ...
T* foo;
// ...
}
Is it possible to assign the argument in both constructors to foo? If so, what would be the correct syntax?
EDIT:
In a more specific case, I have a class myClass that wraps around std::vector and allows to me to directly access subsets of a vector through a nested class mySubset:
template<typename _type>
myClass() {
std::vector<_type> data;
public:
class mySubset(){
myClass<type>* foo;
public:
mySubset(myClass<_type>* _in) { foo = _in; };
mySubset(const myClass<_type>* _in) { foo = _in; /* error */ };
// ...
}
// ...
myClass();
// ...
void mySubset method() { return mySubset(this); };;
void mySubset const_method const() { return mySubset(this); /* error */ };
// ...
}
The code within is irrelevant -basically mySubset allows to both read and write to specific vector positions. While I'm able to achieve what I want with separate const and non-const nested classes, I was looking for a way to do this with a single return type.
I think you'll have to reconsider your design since you can't initialize a T* with a const T* lvalue, without const_cast which should be avoided unless you're really really sure, (since it invokes an undefined behavior if you try to modify a const pointer after casting away its constness)
Instead, you could use template type deduction for const and non const
template <typename T>
class myClass {
public:
//myClass(T* v):foo(v) { }
myClass( T* v):foo(v)
{
}
// ...
T* foo;
// ...
};
Then,
int a =42;
const int* p1 = &a;
int *p2 = &a;
myClass X1(p1); //C++17 auto type deduction or use myClass<const int> X1(p1)
myClass X2(p2);
You could using const_cast in your const T* constructor, but typically you shouldn't.
const T* means "point to a constant value T", and you store a "pointer to a T". If you do a const cast, you could end up modifying a value which shouldn't be modified. If you aren't going to modify foo, just declare it const T* and just use the single const T* constructor.
I'd check to see if this is a design issue. A lot of the times these scenarios appear:
(1) Where you're storing a pointer to something as non-const when it should be const. Typically this is because you're accessing values in another object and you should be passing the object as a (possibly const) reference at each use site rather than storing a pointer to it.
(2) When you really want to store a copy of an object, in which case you just keep a regular T and pass it in as const T& in the constructor.
(3) You're dealing with raw C-style strings and want to copy the contents into your own buffer.
If you don't want to use parameterized-type (template) class as #P0W's answer, it is not possible you can use only one pointer to accept all constant and non-constant pointer type. You need another constant pointer type to accept only const <your another class> * in your wrapper class.
Below code works after you have two separate pointer types in wrapper class which you may not like.
#include <iostream>
using namespace std;
class SomeObject {
public:
SomeObject(){}
explicit SomeObject(int i):testVal(i){}
private:
int testVal;
};
class PtWrapper {
public:
PtWrapper(SomeObject *pso);
PtWrapper(const SomeObject *cpso);
private:
SomeObject *pSO;
const SomeObject *cpSO;
};
int main(int argc, char *argv[]) {
SomeObject so(133);
SomeObject *pso = &so;
const SomeObject cso(166);
const SomeObject *cpso = &cso;
PtWrapper pw1(pso);
PtWrapper pw2(cpso);
return 0;
}
PtWrapper::PtWrapper(SomeObject *pso) :pSO(pso){
}
PtWrapper::PtWrapper(const SomeObject *cpso):cpSO(cpso){}
Related
Consider a tempalted class InputBuffer:
template<class Source, size_t Capacity>
class InputBuffer
{
public:
explicit InputBuffer(Source src);
int getchar();
private:
std::byte const* m_read_ptr;
std::byte const* m_last_valid;
Source m_src;
std::array<std::byte, Capacity> m_data;
void fetchAndResetPointers();
};
Question: Should the constructor instead accept src as a reference and store a pointer instead of taking it by value? It is very likely that the caller expect reference semantics here. However, it is also possible that Source already is some kind of pointer, and then, taking src by reference and storing a pointer would lead to an unnecessary indirection. If not passing by reference, the user can use std::ref if needed.
This class is no good as is in my opinion - you assume that "source" requires an m_read_ptr, m_last_valid and m_data as context. However, if it's a file, for instance, it requires non of these. Instead, rewrite this class as an interface, or better yet, don't create a generic class at all and use templates when handling "sources", example in pseudo-code:
class FileBuffer {
public:
explicit FileBuffer(File* f) : m_f(f) {}
int getchar() { return read(f, 1); }
private:
File* m_f;
};
template<class T>
void print_from_buffer_to_stdout(T& buf) {
std:: cout << buf.getchar();
}
int main() {
FileBuffer f = get_file_buffer(); // somehow
print_from_buffer_to_stdout(f);
}
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*.
Say I have a simple class like this
class Foo
{
public:
void foo()const
{
str[5] = 'x';
obj->changeTheWorld();
x = 4;
y.get() = 5;
obj2->changeTheWorld();
}
private:
char *str; //some referenced data, not owned by Foo
ComplexObj *obj; //some referenced data, not owned by Foo
int &x; //references as well
//wrapped reference, but has a "T& get()const"
std::reference_wrapper<int> y;
//an occasionally useful pointer wrapper for complex memory cases
//but has a "T* get()const"
std::shared_ptr<ComplexObj> obj2;
};
This is valid because in the const method, its just the pointer itself that becomes const, not the data it points to. However in many cases that is not what I desired and I want a compile error if a const method tries to change these members contents (either directly or by calling a non-const method on that member).
Is there a standard solution to this?
I think some kind of wrapper class should be able to achieve this, and should also be something the compiler optimises out, although haven't sat down to try and design such a thing to cover all cases giving say a strong_const<char*> str and strong_const<int&> (also not sure on a good name...).
Well, neither std::reference_wrapper nor std::shared_ptr do not provide const propagation, so they are not more "const-strict" than regular pointer.
I'd recommend to make your own const propagation class (I am not sure - maybe something similar is already provided by boost - please let me know in comments)
My proposition is this class:
#include <memory> // for pointer_traits
template <typename Pointer>
class ConstPropagatePointer
{
public:
using element_type = typename std::pointer_traits<Pointer>::element_type;
using pointer = typename std::pointer_traits<Pointer>::pointer;
using const_pointer = element_type const * const;
using reference = element_type&;
using const_reference = element_type const&;
ConstPropagatePointer(Pointer ptr) : ptr(ptr) {}
pointer operator -> ()
{
return &(*ptr);
}
const_pointer operator -> () const
{
return &(*ptr);
}
reference operator * ()
{
return *ptr;
}
const_reference operator * () const
{
return *ptr;
}
private:
Pointer ptr;
};
So that will work for you:
class Foo
{
public:
private:
ConstPropagatedPointer<char*> str;
ConstPropagatedPointer<ComplexObj*> obj;
ConstPropagatedPointer<std::shared_ptr<ComplexObj>> obj2;
};
I am creating a class which interops with some Windows API code, now one of the pointers I have to initialize is done by calling a native function which initializes it.
My pointers are of type std::unique_ptr with a custom deleter, which calls the WinAPI deleter function provided, however I cannot pass the unique_ptr with the & address-of operator to the init-function. Why?
I have created a sample that demonstrates my problem:
#include <memory>
struct foo
{
int x;
};
struct custom_deleter {};
void init_foo(foo** init)
{
*init = new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&foo_ptr);
}
The compiler barks and says:
source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
Somewhere under the covers, unique_ptr<foo> has a data member of type foo*.
However, it's not legitimate for a user of the class to directly modify that data member. Doing so would not necessarily preserve the class invariants of unique_ptr, in particular it wouldn't free the old pointer value (if any). In your special case you don't need that to happen, because the previous value is 0, but in general it should happen.
For that reason unique_ptr doesn't provide access to the data member, only to a copy of its value (via get() and operator->). You can't get a foo** out of your unique_ptr.
You could instead write:
foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
This is exception-safe for the same reason that std::unique_ptr<foo, custom_deleter> foo_ptr(new foo()); is exception-safe: unique_ptr guarantees that whatever you pass in to its constructor will eventually get deleted using the deleter.
Btw, doesn't custom_deleter need an operator()(foo*)? Or have I missed something?
Steve has already explained what the technical problem is, however, the underlying problem goes much deeper: The code employs an idiom helpful when you deal with naked pointers. Why does this code do two-step initialization (first create the object, then initialize it) in the first place? Since you want to use smart pointers, I'd suggest you carefully adapt the code:
foo* init_foo()
{
return new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );
}
Of course, renaming init_foo() to create_foo() and having it return a std::unique_ptr<foo> directly would be better. Also, when you use two-step initialization, it's often advisable to consider using a class to wrap the data.
You can use the following trick:
template<class T>
class ptr_setter
{
public:
ptr_setter(T& Ptr): m_Ptr{Ptr} {}
~ptr_setter() { m_Ptr.reset(m_RawPtr); }
ptr_setter(const ptr_setter&) = delete;
ptr_setter& operator=(const ptr_setter&) = delete;
auto operator&() { return &m_RawPtr; }
private:
T& m_Ptr;
typename T::pointer m_RawPtr{};
};
// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.
#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)
and then:
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
I eventually came up with an approach that allows to initialise unique_ptr's with a code like this:
struct TOpenSSLDeleter { ... }; // Your custom deleter
std::unique_ptr<EVP_MD_CTX, TOpenSSLDeleter> Ctx;
...
Ctx = MakeUnique(EVP_MD_CTX_create()); // MakeUnique() accepts raw pointer
And here is the solution:
template <class X>
struct TUniquePtrInitHelper {
TUniquePtrInitHelper(X *Raw) noexcept {
m_Raw = Raw;
}
template <class T, class D>
operator std::unique_ptr<T, D>() const noexcept {
return std::unique_ptr<T, D>(m_Raw);
}
private:
X *m_Raw;
};
template <class X>
TUniquePtrInitHelper<X> MakeUnique(X *Raw) noexcept {
return {Raw};
}
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;
};