I would like to overload the operator<< to allow it to work with shared_ptr.
template<typename T>
struct foo
{
virtual foo& operator<<(const T& e) = 0;
};
foo<int> f1;
f1 << 1;
std::shared_ptr<foo<int>> f2(new foo<int>());
f2 << 1;
My first try is the following, but the problem is that it with also enable the behavior for any class.
template<typename T, typename U>
const std::shared_ptr<T>& operator<<(const std::shared_ptr<T>& o, const U& e)
{
*o << e;
return o;
}
My second try is the following:
template<typename T, typename U>
const std::shared_ptr<foo<T>>& operator<<(const std::shared_ptr<foo<T>>& o, const U& e)
{
*o << e;
return o;
}
The problem with this solution is not work for types inheriting foo since T cannot be automatically deduced.
So I could skip U and use T instead, in which case T will be deduced from the second argument and the argument for o can be converted into foo<T>.
template<typename T, typename U>
const std::shared_ptr<foo<T>>& operator<<(const std::shared_ptr<foo<T>>& o, const T& e)
{
*o << e;
return o;
}
But then the following will not work:
struct c
{
};
struct a
{
a();
a(c); // implicit conversion
};
struct b
{
operator a(); // implicit conversion
};
auto f = std::make_shared<foo<a>>();
f << c; // doesn't work.
f << b; // doesn't work.
Any ideas on how make a working solution?
Some options, see the second live at https://ideone.com/26nqr
Given
#include <iostream>
#include <memory>
using namespace std;
template<typename T> struct foo {
virtual foo& operator<<(const T& e) const { std::cout << "check foo\n"; }
};
//////
// derived instances
struct derived : foo<int> {
virtual derived& operator<<(const int& e) const { std::cout << "check derived\n"; }
};
template<typename T> struct genericDerived : foo<T> {
virtual derived& operator<<(const T& e) const { std::cout << "check genericDerived\n"; }
};
template<typename T, typename U, template <typename> class X>
const std::shared_ptr<X<T>>& operator<<(const std::shared_ptr<X<T>>& o, const U& e)
{
*o << e;
return o;
}
int main()
{
auto f = make_shared<foo<int>>();
f << 1;
auto d = make_shared<derived>();
d << 2; // compile error
auto g = make_shared<genericDerived<int>>();
g << 3; // SUCCESS!
}
The above doesn't catch derived classes (case 2). To do that, I'd resort to
#include <type_traits>
namespace detail
{
template<typename Foo, typename T>
const std::shared_ptr<Foo>& dispatch_lshift(
const std::shared_ptr<Foo>& o, const T& e,
const std::true_type& enabler)
{
*o << e;
return o;
}
}
template<typename Foo, typename T>
const std::shared_ptr<Foo>& operator<<(const std::shared_ptr<Foo>& o, const T& e)
{
return detail::dispatch_lshift(o, e, std::is_convertible<Foo*, foo<T>* >());
}
int main()
{
auto f = make_shared<foo<int>>();
f << 1;
auto d = make_shared<derived>();
d << 2;
auto g = make_shared<genericDerived<int>>();
g << 3;
auto x = make_shared<int>();
// x << 4; // correctly FAILS to compile
}
May be you're putting yourself in a bad arena, of making C++ to look like Java.
C++ pointers, values, and references are different types, and have different semantics and operations. It is not in general a good idea to mix them up into a same syntax. Pointers are not their values: they can point to types other than the one they refer (typical, in case of inheritance). So it is in general better to let pointer dereference to remain explicit.
Neverless, saving to a stream a "pointer value" (not "pointe*d* value") has no interest, since that value (a memory address) will be meaningless on stream reading.
If you properly want to "save what is pointed", when saving a pointer you should save something tells "what type of object you're saving", an then save the object polymorphically, (through a call to a virtual function) o that also the derived data are saved. Meanwhile, you also have to track circular references, in order not to save a same object multiple times (and loading it back as distinct objects), so an "identity" is also required.
To load the object back, you should first read the metadata you saved to describe the type, and based on that, create a corresponding object, then reassign its members to the data loaded on extraction. (Or if just an identity was saved, point the pointer to the already recreated corresponding object).
All that stuffs goes under the name of serialization. There are many solution around, you should probably Google for that key.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With