防止模板化类的多个实例化

时间:2019-03-05 04:02:43

标签: c++ templates

我有模板课程,例如:

template <int X>
struct Foo {
  static int get() {return X;}
};

我当然可以显式实例化所需的版本:

template class Foo<1>;

如果要尝试第二次显式实例化,我想在编译时生成错误。

template class Foo<1>;
template class Foo<2>; // How to error here at compile time?

这可能吗?

我怀疑如果在多个翻译单元中进行编译,则需要使用一些“重新定义”技巧来使链接器捕获此错误。我一生无法弄清楚这是否可行,或如何做到。

如果有一种方法可以执行,而无需显式模板实例化?


上下文

我正在编写一个完全静态的类库,以管理所用微控制器上的某些硬件。我想轻松更改编译时参数(X),因此正在使用模板。 #define是不可接受的。 constexpr无法正常工作,您将如何#include从属源文件?

具体地说,我有一个init()函数只能运行一次,而我实际上是在使用__attribute__((constructor))来强制它在main()之前运行。如果该库的其他一些用户不经意地实例化了第二个实例,则会发生不好的事情。

2 个答案:

答案 0 :(得分:0)

您可以将您的类模板封装为私有的嵌套类模板,然后仅将要创建的实例公开为公共成员:

class Foo {
private:
    template<int X>
    struct BarImpl {
        static int get() { return X; }
    };

public:
    using Bar = BarImpl<1>;
};

然后您可以像这样使用它:

int i = Foo::Bar::get();
//int j = Foo::BarImpl<2>::get(); // Error, BarImpl is private

此外,由于您知道将仅创建模板的一个实例,并且知道哪个实例,因此您可以(但不必这样做)将.hpp和像这样的.cpp文件:

// Foo.hpp
class Foo {
private:
    template<int X>
    struct BarImpl {
        static int get();
    };

    static constexpr int Y = 1;
public:
    using Bar = BarImpl<Y>;
};

// Foo.cpp
template<>
int Foo::Bar::get() {
    return Y;
}

由于无法通过X访问Foo::Bar,因此必须将参数保存在某个地方(在Y中)。如果您无法使用constexpr,则可以将其设置为const。这还具有命名参数的优点,而不仅仅是具有“魔术值”。

答案 1 :(得分:0)

我非常确定不可能在翻译单元之间执行此操作,因为您必须从模板中生成一个非弱符号。在翻译部门中,这很容易:

template<int>
struct Foo {
  friend void highlander();
};

在一个翻译单元中重复实例化Foo的格式不正确,因为highlander的定义只有一个。 (方便地,永远无法引用该函数,因为它仅对ADL可见,并且没有参数。)

您当然也可以通过为每个专业指定不同的返回类型来使它跨翻译单元变形,但是这样就不需要诊断了,在实践中也将一无所获。

相关问题