繁体   English   中英

如何为引用和非引用类型制作 class 模板专业化?

[英]How to make a class template specialization for reference and non-reference types?

我有一个名为Pen的 class :

class Pen {
    int m_color;

public:
    Pen(const int &color) 
        :m_color(color) {}
};

我想要一个名为Drawable的 class 来存储Pen 如果用户将Pen的左值引用传递给Drawable的构造函数,那么我想将该Pen作为引用存储在Drawable中。 如果我得到一个右值引用,我想将该Pen作为一个新变量存储在Drawable中。

换句话说,如果我得到一个已经存在的变量,那么我想将其存储为引用,否则将其内容复制(或移动)到一个新变量中。

我想这样做,以便如果用户更改Pen的任何属性,这些更改也应该自动反映在我的Drawable中的Pen上。

到目前为止,这是我设法提出的:

template<bool isRef>
class Drawable {};

template<>
class Drawable<true> {
    Pen &m_pen;

public:
    Drawable(Pen &a)
        :m_pen(a) {}
};

template<>
class Drawable<false> {
    Pen m_pen;
    
public:
    Drawable(Pen &&a)
        :m_pen(std::move(a)) {}
};

但是,在这种情况下,用户每次都必须指定真或假,我觉得这有点不方便。 有没有一种方法可以通过使用模板或完全不同的方式实现自动化?

只是一个旁注。 这将是其他形状的基础 class。 C++ 标准:任意。 大部分时间将使用 C++20。

一种替代方法是使用共享指针。 这样做的好处是只有一个Drawable类型(而Drawable<true>Drawable<false>是单独的类型)。 Drawable不需要知道或关心是否有另一个笔的所有者:

class Drawable {
    std::shared_ptr<Pen> m_pen;
public:
    Drawable(std::shared_ptr<Pen> pen): m_pen(pen) {}
};

用户可以使用稍后更改的钢笔制作Drawable

auto pen = std::make_shared<Pen>(...);
Drawable d(pen);
pen->setColor(...);

或者使用它提供给Drawable的新笔:

Drawable d(std::make_shared<Pen>(...));

无论如何,它们是不同的类型,那么为什么不简单地保留一个模板并使用辅助函数来创建正确的模板呢?

template <typename Pen>
struct Drawable
{
    Pen pen;
};

Drawable<Pen&> makeDrawable(Pen& pen)
{
    return {pen};
}

Drawable<Pen> makeDrawable(Pen&& pen)
{
    return {std::move(pen)};
}

使用auto获取合适的类型也同样简单:

Pen p;
auto d1 = makeDrawable(p);
auto d2 = makeDrawable(Pen());

std::cout << typeid(d1).name() << std::endl;
std::cout << typeid(d2).name() << std::endl;

我的系统(linux,gcc)上的 Output:

    8DrawableIR3PenE
    8DrawableI3PenE

所以我找到了一种适合我需要的方法,这基本上是@Kevin 的回答和@Sam Varshavchik 对这个问题的评论的结合。 所以我将std::shared_ptr与 Class 模板参数推导指南一起使用。 这是我的解决方案:

template<typename> // Base Condition 
class Drawable {};

/* Lvalue */
template<>
class Drawable<Pen &> {
    Pen &m_pen;

public:
    Drawable(Pen &a)
        :m_pen(a) {}
};

/* Rvalue */
template<>
class Drawable<Pen &&> {
    Pen m_pen;
    
public:
    Drawable(Pen &&a)
        :m_pen(std::move(a)) {}
};

/* Shared */
template<>
class Drawable<std::shared_ptr<Pen>> {
    std::shared_ptr<Pen> m_pen;
    
public:
    Drawable(std::shared_ptr<Pen> pen)
        :m_pen(pen) {}
};

/* These guides help out the compiler determine the right specialization */
Drawable(Pen &) -> Drawable<Pen &>;
Drawable(Pen &&) -> Drawable<Pen &&>;
Drawable(std::shared_ptr<Pen>) -> Drawable<std::shared_ptr<Pen>>;

所有这一切,是我想要支持的 3 种情况的 3 种专业化,以及帮助编译器决定编译器应该选择哪一种的指南。 现在,用户可以将现有的或临时的 Pen 或现有的或临时的std::shared_ptr传递给 Pen,一切都可以正常工作,而无需用户指定任何模板 arguments。

要删除重复代码,只需将其移动到基础 class 中,然后在所有 3 种情况下都继承它。

顺便说一句,模板参数不必与您传入的内容完全匹配。我只是这样做是为了使代码更具可读性(ig)。 你也可以通过做这样的事情来逃脱。

template<int>
class Drawable {};

/* Lvalue */
template<>
class Drawable<1> {
    Pen &m_pen;

public:
    Drawable(Pen &a)
        :m_pen(a) {}

    Pen &get() { return m_pen; }
};

/* Rvalue */
template<>
class Drawable<2> {
    Pen m_pen;
    
public:
    Drawable(Pen &&a)
        :m_pen(std::move(a)) {}

    Pen &get() { return m_pen; }
};

/* Shared */
template<>
class Drawable<3> {
    std::shared_ptr<Pen> m_pen;
    
public:
    Drawable(std::shared_ptr<Pen> pen)
        :m_pen(pen) {}

    std::shared_ptr<Pen> &get() { return m_pen; }
};


Drawable(Pen &) -> Drawable<1>;
Drawable(Pen &&) -> Drawable<2>;
Drawable(std::shared_ptr<Pen>) -> Drawable<3>;

这里不是使用类型而是模板参数,我使用数字也不要使用这种方法,因为你不明白发生了什么。 这只是一个例子来说明一个观点。

所以现在你的main()看起来像这样:

int main() {
    Pen ref(69);

    Drawable a(Pen(69));
    Drawable b(ref);
    Drawable c(std::make_shared<Pen>(33));

    ref = 420; // Now the Pen inside Drawable is also 420
}

感谢@Kevin、@Sam Varshavchik 和@Aconcagua 的帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM