我想重载运算符<<允许它与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;
我的第一次尝试是以下内容,但问题是它还可以启用任何类的行为。
template<typename T, typename U>
const std::shared_ptr<T>& operator<<(const std::shared_ptr<T>& o, const U& e)
{
*o << e;
return o;
}
我的第二次尝试如下:
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;
}
此解决方案的问题不适用于继承foo的类型,因为T
无法自动推断出来。
所以我可以跳过U
并使用T
代替,在这种情况下,T将从第二个参数中推断出来,o
的参数可以转换为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;
}
但是以下内容不起作用:
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.
关于如何制定有效解决方案的任何想法?
答案 0 :(得分:3)
有些选项,请参阅 https://ideone.com/26nqr
中的第二个鉴于
#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!
}
上面没有捕获派生类(案例2)。要做到这一点,我会诉诸
#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
}
答案 1 :(得分:1)
可能是你把自己置于糟糕的舞台中,使C ++看起来像Java。
C ++指针,值和引用是不同的类型,并且具有不同的语义和操作。将它们混合到相同的语法中通常不是一个好主意。指针不是它们的值:它们可以指向除它们所引用的类型之外的类型(典型的,在继承的情况下)。因此,通常最好让指针取消引用保持显式。
永远不会,保存到流“指针值”(不是“pointe * d * value”)没有兴趣,因为该值(内存地址)在流读取时将毫无意义。
如果你想要“保存指向的东西”,在保存指针时你应该保存一些东西告诉“你正在保存什么类型的对象”,然后以多态方式保存对象,(通过调用虚拟函数) )o也保存派生数据。同时,您还必须跟踪循环引用,以便不多次保存同一个对象(并将其作为不同的对象加载),因此也需要“标识”。
要重新加载对象,首先应读取保存的元数据以描述类型,然后根据该元素创建相应的对象,然后将其成员重新分配给提取时加载的数据。 (或者,如果仅保存了标识,请将指针指向已重新创建的对应对象)。
所有这些东西都以序列化的名义命名。有许多解决方案,你可能应该谷歌那个密钥。
答案 2 :(得分:-1)
您可以使用enable_if
的第一个解决方案(我使用C ++ 03命名空间):
template <typename T>
class is_funky : public boost::false_type {};
template <typename S>
class is_funky< Foo<S> > : public boost::true_type {};
template<typename T, typename U>
typename boost::enable_if< is_funky<T>, const boost::shared_ptr<T>& >::type
operator<<(const boost::shared_ptr<T>& o, const U& e)
{
*o << e;
return o;
}
您可能希望在生产代码中找到比is_funky
更合适的名称,但这取决于上下文。