运营商LT;<智能指针的过载

时间:2012-01-02 12:56:19

标签: c++ visual-studio-2010 c++11 template-meta-programming typetraits

我想重载运算符<<允许它与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.

关于如何制定有效解决方案的任何想法?

3 个答案:

答案 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!
}

完成者:SFINAE

上面没有捕获派生类(案例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更合适的名称,但这取决于上下文。