How to make a pointer member const'able [duplicate] - c++

I have a class like this one:
struct Example
{
std::unique_ptr<int> pointer_member;
};
I'd like the type of pointer_member to be std::unique_ptr<int const> const when manipulating instances of Example const, and std::unique_ptr<int> when manipulating instances of Example.
The best solution I could think of so far involves templates, but it's really boilerplaty and not very usable (because the template arguments propagate to the code working with Example):
template <bool is_const>
struct Example
{
std::unique_ptr<std::conditional_t<is_const, int const, int>> pointer_member;
};
Also here, nothing prevents me from using Example<false> const instances and it's really annoying.
Any idea of how to achieve this in a better way?

Encapsulation allows to control modifiable-ness (aka constness) of members:
#include <memory>
struct Example {
const int& get() const {
return *pointer_member;
}
int& get() {
return *pointer_member;
}
private:
std::unique_ptr<int> pointer_member = std::make_unique<int>(42);
};
int main(){
const Example ex1;
const int& const_ref = ex1.get();
Example ex2;
ex2.get() = 123;
}

Related

Map with const and non-const values

I'm currently looking at some code that uses instances of both of the following types of maps:
std::map<std::string, const Foo>;
std::map<std::string, Foo>;
where the implementation of Foo is not relevant. As a result, all of the functions that take as input an instance of either of these two have two implementations of them: one for the const Foo version and the other for the non-const Foo version.
My question is this: is it possible to create something (like a class derived from std::map, for example) using templates that will allow me to essentially encapsulate both versions of the types above as one?
Edit: I know I can probably use SFINAE to only have to write one version of each of the relevant functions, but I'm wondering if there's a way to implement something a little bit further "upstream."
I apologize if my question doesn't make sense - I'm not exactly sure how to word it in a nice way.
A function that can accept both the const and non const versions of your map could be implemented in this manner:
template <typename T_foo>
T_foo do_something_with_map(std::map<std::string, T_foo> & map)
{
std::cout << map["m"].i_ << std::endl;
return map["m"];
}
int main()
{
std::map<std::string, const Foo> m1;
Foo f1(1);
m1.emplace("m", f1);
std::map<std::string, Foo> m2;
const Foo f2(2);
m2.emplace("m", f2);
auto res1 = do_something_with_map(m1);
auto res2 = do_something_with_map(m2);
}
With an example Foo:
class Foo {
public:
Foo() = default;
Foo(int i)
: i_(i)
{}
int i_;
};

Virtually turn vector of struct into vector of struct members

I have a function that takes a vector-like input. To simplify things, let's use this print_in_order function:
#include <iostream>
#include <vector>
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<int> printme = {100, 200, 300};
std::vector<int> order = {2,0,1};
print_in_order(order, printme);
}
Now I have a vector<Elem> and want to print a single integer member, Elem.a, for each Elem in the vector. I could do this by creating a new vector<int> (copying a for all Elems) and pass this to the print function - however, I feel like there must be a way to pass a "virtual" vector that, when operator[] is used on it, returns this only the member a. Note that I don't want to change the print_in_order function to access the member, it should remain general.
Is this possible, maybe with a lambda expression?
Full code below.
#include <iostream>
#include <vector>
struct Elem {
int a,b;
Elem(int a, int b) : a(a),b(b) {}
};
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)};
std::vector<int> order = {2,0,1};
// how to do this?
virtual_vector X(printme) // behaves like a std::vector<Elem.a>
print_in_order(order, X);
}
It's not really possible to directly do what you want. Instead you might want to take a hint from the standard algorithm library, for example std::for_each where you take an extra argument that is a function-like object that you call for each element. Then you could easily pass a lambda function that prints only the wanted element.
Perhaps something like
template<typename vectorlike, typename functionlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme,
functionlike func) {
for (int i : order)
func(printme[i]);
}
Then call it like
print_in_order(order, printme, [](Elem const& elem) {
std::cout << elem.a;
});
Since C++ have function overloading you can still keep the old print_in_order function for plain vectors.
Using member pointers you can implement a proxy type that will allow you view a container of objects by substituting each object by one of it's members (see pointer to data member) or by one of it's getters (see pointer to member function). The first solution addresses only data members, the second accounts for both.
The container will necessarily need to know which container to use and which member to map, which will be provided at construction. The type of a pointer to member depends on the type of that member so it will have to be considered as an additional template argument.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
private:
const Container * m_container;
MemberPtr m_member;
};
Next, implement the operator[] operator, since you mentioned that it's how you wanted to access your elements. The syntax for dereferencing a member pointer can be surprising at first.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// Dispatch to the right get method
auto operator[](const size_t p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To use this implementation, you would write something like this :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a);
print_in_order(order, X);
}
This is a bit cumbersome since there is no template argument deduction happening. So lets add a free function to deduce the template arguments.
template<class Container, class MemberPtr>
virtual_vector<Container, MemberPtr>
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr)
{
return{ p_container, p_member_ptr };
}
The usage becomes :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
If you want to support member functions, it's a little bit more complicated. First, the syntax to dereference a data member pointer is slightly different from calling a function member pointer. You have to implement two versions of the operator[] and enable the correct one based on the member pointer type. Luckily the standard provides std::enable_if and std::is_member_function_pointer (both in the <type_trait> header) which allow us to do just that. The member function pointer requires you to specify the arguments to pass to the function (non in this case) and an extra set of parentheses around the expression that would evaluate to the function to call (everything before the list of arguments).
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// For mapping to a method
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const
{
return ((*m_container)[p_index].*m_member)();
}
// For mapping to a member
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To test this, I've added a getter to the Elem class, for illustrative purposes.
struct Elem {
int a, b;
int foo() const { return a; }
Elem(int a, int b) : a(a), b(b) {}
};
And here is how it would be used :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
{ // print member
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
{ // print method
auto X = make_virtual_vector(printme, &Elem::foo);
print_in_order(order, X);
}
}
You've got a choice of two data structures
struct Employee
{
std::string name;
double salary;
long payrollid;
};
std::vector<Employee> employees;
Or alternatively
struct Employees
{
std::vector<std::string> names;
std::vector<double> salaries;
std::vector<long> payrollids;
};
C++ is designed with the first option as the default. Other languages such as Javascript tend to encourage the second option.
If you want to find mean salary, option 2 is more convenient. If you want to sort the employees by salary, option 1 is easier to work with.
However you can use lamdas to partially interconvert between the two. The lambda is a trivial little function which takes an Employee and returns a salary for him - so effectively providing a flat vector of doubles we can take the mean of - or takes an index and an Employees and returns an employee, doing a little bit of trivial data reformatting.
template<class F>
struct index_fake_t{
F f;
decltype(auto) operator[](std::size_t i)const{
return f(i);
}
};
template<class F>
index_fake_t<F> index_fake( F f ){
return{std::move(f)};
}
template<class F>
auto reindexer(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return v[f(i)];
});
};
}
template<class F>
auto indexer_mapper(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return f(v[i]);
});
};
}
Now, print in order can be rewritten as:
template <typename vectorlike>
void print(vectorlike const & printme) {
for (auto&& x:printme)
std::cout << x << std::endl;
}
template <typename vectorlike>
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) {
print(reindexer([&](auto i){return reorder[i];})(printme));
}
and printing .a as:
print_in_order( reorder, indexer_mapper([](auto&&x){return x.a;})(printme) );
there may be some typos.

implicitly convert std::shared_ptr to a type

Let's say I have a class A which specifies conversion to int
struct A {
int val = 42;
operator int() const {
return val;
}
so I can use it like this:
A a;
int a_int = a;
But what if I want to use shared pointer to the class:
auto a_ptr = std::shared_ptr<A>(new A);
int a_int = a_ptr; // ???
Just like the variable with type A was implicitly converted to int I want to do the same with a smart pointer.
How can I achieve that?
UPDATE
I'm sorry maybe I asked wrong way.
The real deal is a little bit more complicated.
I use QVariant to hold std::shared_ptr.
for now I have a helper function to do that:
QVariant & operator<< (QVariant& v, const std::shared_ptr<A> & aPtr);
When I need to place my pointer to QVariant I do this:
QVariant variant = QVariant() << a_ptr;
But I want to do it automatically, something like this:
QVariant variant = a_ptr;
Have you tried it?
std::shared_ptr has the operator* and it works as one can expect: use it.
#include <memory>
struct A {
int val = 42;
operator int() const {
return val;
}
};
int main() {
auto ptr = std::make_shared<A>();
int v = *ptr;
}
Just like the variable with type A was implicitly converted to int I want to do the same with a smart pointer.
No, you think you do, but you really don't.
How can I achieve that?
You can't, and that's a good thing.
(anticipating) Any way around it?
Wrap the shared pointer into an object, like this:
#include <memory>
template<class T>
struct shared_reference
{
shared_reference(std::shared_ptr<T> pt) : _pt(std::move(pt)) {}
operator T& () {
return *_pt;
}
operator const T& () const {
return *_pt;
}
std::shared_ptr<T> _pt;
};
Its quite simple, use dereference operator:
int a_int = *a_ptr; // ???
^ ~~~
[Edit]
To make:
QVariant variant = a_ptr;
Work, You would have to add to QVariant copy constructor accepting reference to shared_ptr. And this is something you cannot do. Such statement is called Copy Initialization, first compiler tries to do conversion of a_ptr to QVariant and if it is available then copy constructor of QVariant is called with converted a_ptr. The problem is that even if you dereference a_ptr, there are two user defined conversions needed.
You can still add a cast like here:
QVariant variant = static_cast<int>(*a_ptr);

How do I initialize a std::set comparator?

I need to initialize some comparator of the new data type TType based on std::set with some object o of another class Object:
typedef std::set <unsigned int, sortSet(o)> TType
This declaration is otside the class (in header file). At the time of the declaration this object does not have to exist, it will be created later.
class sortSet
{
private:
Object o;
public:
sortSet(Object &oo): o(oo) {}
bool operator() ( const unsigned int &i1, const unsigned int &i2 ) const
{
//Some code...
}
};
If the declaration was inside some method (when object has already been created), situation would be quite simple... What can I do?
The template parameter needs to be the type of the comparator, not a specific object of that type; you can provide a specific comparator object to be used in the std::set constructor:
typedef std::set<unsigned int, sortSet> TType;
Object o;
TType mySet(sortSet(o));
I am not sure if I understood your actual question, however, the general idiom to use a custom comparator is shown in the following contrived example.
#include <set>
class foo {
public:
int i_;
};
struct foo_comparator {
bool operator()( foo const & lhs, foo const & rhs ) {
return lhs.i_ < rhs.i_;
}
};
typedef std::set< foo, foo_comparator > foo_set;
int main() {
foo_set my_foo_set;
}

Overloaded [] operator on template class in C++ with const / nonconst versions

Whew, that was a long title.
Here's my problem. I've got a template class in C++ and I'm overloading the [] operator. I have both a const and a non-const version, with the non-const version returning by reference so that items in the class can be changed as so:
myobject[1] = myvalue;
This all works until I use a boolean as the template parameter. Here's a full example that shows the error:
#include <string>
#include <vector>
using namespace std;
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}
The error is a compiler error and the message is:
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
I've read up and found that STL uses some temporary data types, but I don't understand why it works with everything except a bool.
Any help on this would be appreciated.
Because vector<bool> is specialized in STL, and does not actually meet the requirements of a standard container.
Herb Sutter talks about it more in a GOTW article: http://www.gotw.ca/gotw/050.htm
A vector<bool> is not a real container. Your code is effectively trying to return a reference to a single bit, which is not allowed. If you change your container to a deque, I believe you'll get the behavior you expect.
A vector<bool> is not implemented like all other vectors, and does not work like them either. You are better off simply not using it, and not worrying if your code can't handle its many peculiarities - it is mostly considered to be A Bad Thing, foisted on us by some unthinking C++ Standard committee members.
Some monor changes to your class should fix it.
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
// This works better if you pass by const reference.
// This allows the compiler to form temorary objects and pass them to the method.
void add(T const& item)
{
_items.push_back(item);
}
// For the const version of operator[] you were returning by value.
// Normally I would have returned by const ref.
// In normal situations the result of operator[] is T& or T const&
// But in the case of vector<bool> it is special
// (because apparently we want to pack a bool vector)
// But technically the return type from vector is `reference` (not T&)
// so it you use that it should compensate for the odd behavior of vector<bool>
// Of course const version is `const_reference`
typename vector<T>::const_reference operator[](int idx) const
{
return _items[idx];
}
typename vector<T>::reference operator[](int idx)
{
return _items[idx];
}
};
As the other answers point out, a specialization is provided to optimize for space allocation in the case of vector< bool>.
However you can still make your code valid if you make use of vector::reference instead of T&. In fact it is a good practice to use container::reference when referencing data held by a STL container.
T& operator[](int idx)
becomes
typename vector<T>::reference operator[](int idx)
Of course ther is also a typedef for const reference:
const T operator[](int idx) const
and this one becomes (removing the useless extra copy)
typename vector<T>::const_reference operator[](int idx) const
The reason for the error is that vector<bool> is specialized to pack the boolean values stored within and vector<bool>::operator[] returns some sort of proxy that lets you access the value.
I don't think a solution would be to return the same type as vector<bool>::operator[] because then you'd be just copying over the regrettable special behavior to your container.
If you want to keep using vector as the underlying type, I believe the bool problem could be patched up by using a vector<MyBool> instead when MyClass is instantiated with bool.
It might look like this:
#include <string>
#include <vector>
using namespace std;
namespace detail
{
struct FixForBool
{
bool value;
FixForBool(bool b): value(b) {}
operator bool&() { return value; }
operator const bool& () const { return value; }
};
template <class T>
struct FixForValueTypeSelection
{
typedef T type;
};
template <>
struct FixForValueTypeSelection<bool>
{
typedef FixForBool type;
};
}
template <class T>
class MyClass
{
private:
vector<typename detail::FixForValueTypeSelection<T>::type> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}