【问题标题】:explicit instantiation ... but no definition available [-fpermissive]显式实例化...但没有可用的定义 [-fpermissive]
【发布时间】:2014-03-25 12:59:02
【问题描述】:

我正在尝试使用静态数据成员构造一个模板类,当我尝试编译以下代码时收到此错误消息:

|In instantiation of ‘T<int>& T<int>::t’:|
16|required from here|
16|error: explicit instantiation of ‘T<int>::t’ but no definition available [-fpermissive]|
|In instantiation of ‘T<int>& T<int>::t’:|
16|required from here|
16|error: explicit instantiation of ‘T<int>::t’ but no definition available [-fpermissive]|
||=== Build finished: 2 errors, 4 warnings (0 minutes, 0 seconds) ===|

代码(为了演示问题而提炼出来的。)

template <class A>
class T {
private:
    static T&      t;
public:
    T&   getT() {return t;}
 };

T<int>  i;
template T<int>& T<int>::t;

int main()
{
    i.getT();

    return 0;
}

恐怕我不明白“没有可用的定义”是什么意思。我以为“模板 T&T::t;”将定义静态数据成员。

我在 Linux 上使用 GCC:

hbarta@cypress:~$ c++ --version
c++ (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

感谢您的帮助!

编辑:将静态成员声明为引用显然是一个错误,我已更正。

感谢您为此提供的几个解决方案和建议。我将不得不研究它们并决定哪个最合适。

FWIW,我打算做的是重新发明一个单链表。目标是 Arduino,资源有限,对代码和 RAM 大小很敏感。否则我会非常乐意使用 STL 容器。我正在编写一个单链表,静态数据成员将是一个哨兵,指向列表的头部。列表中的最后一个元素指向哨兵。在第一次迭代中,我开始使用继承来编写它,但很快发现各种派生类没有通用代码。 (它们在概念上具有相似性,但并未转化为实现,例如,它们具有不同的列表和不同的处理,因此在我看来继承是一个糟糕的初始选择。)希望这种困难不是模板同样不是一个好的标志选择。

【问题讨论】:

  • 有点题外话:你在这里展示的是一个单例,应该小心使用并且很少使用,因为它会导致相对紧密的耦合。如今,单例通常被认为是代码异味(或设计异味)。它是一个模板表明您打算使用多个模板 - 所以它闻起来更香。而且实现方式不是线程安全的——你可能想查找“Meyers Singleton”。
  • 也许题外话,但仍然有用。我想您指的是 Effective C++ 中的提及。我会看看它。我很好奇这里的具体错误,但对其他解决方案持开放态度。
  • 好吧,从您提供的提炼代码中看不到您尝试用单例解决的问题,这没关系,因为问题是关于编译器错误的。如果您有兴趣(替代方案)解决您正在使用单例的设计问题,请随时打开另一个问题,了解您当前使用单例的上下文和当前设计。
  • 编写一个以静态数据成员作为列表头的单链表意味着,每个模板实例化只能有一个列表,即完全有一个 List&lt;int&gt;。这听起来不是很有用。对于有限的 RAM 大小,请考虑使用std::forward_list。由于它是一个模板,因此不会为您在合理兼容的编译器上使用的方法生成任何代码,从而使代码大小也最小。
  • 每个班级一个列表满足我的需求。我目前正在评估 STL 容器,并将查看 std::forward_list 以了解足迹是什么。谢谢!

标签: c++ templates static-members


【解决方案1】:

您对静态成员的定义是错误的。对于所有 T,您要么必须以模板化的方式进行操作:

template <class A> T<A>& T<A>::t = /* ??? */;

或仅作为整数的特化:

template<> T<int>& T<int>::t = /* ??? */;

后者会给您留下一个问题,即使用除 int 之外的任何其他类型实例化 T 将要求您也定义这些实例化的静态成员。

另外,如果静态成员是一个引用,你需要有一些对象来绑定它,在我的 sn-ps 中用??? 表示。将静态成员设为T&lt;A&gt; 的对象而不是引用将解决该问题。

【讨论】:

    【解决方案2】:

    我认为以前的答案提供了您真正需要的解决方案。但如果您的真正目的是使用显式实例化,则可以如下实现:

    template <class A>
    class T
    {
    private:
        static T t;
    public:
        T& getT() {return t;}
    };
    
    // definition of a static variable
    template <class A>
    T<A> T<A>::t;
    
    // explicit instantiation of a static variable for template argument `int`
    template T<int> T<int>::t;
    

    请注意,在此代码中,T&lt;A&gt; 类型的 object 用作静态变量,而不是对其的 reference

    【讨论】:

    • 是的,引用错误。感谢您提供定义与实例化的代码。
    【解决方案3】:

    我会建议一个更简单的方法用于类中的单例,这对模板特别有用:

    template <class A>
    class T {
    public:
        static T& getT() {
            static T t;
            return t;
        }
    };
    

    这样你就不需要额外的定义了。这非常方便,因为在许多情况下模板只是标题,没有用于定义的源文件。

    否则,您应该在声明中删除&amp;

    template <class A>
    class T {
    private:
        static T      t;
    public:
        T&   getT() {return t;}
     };
    
    T<int>  i;
    template<> T<int> T<int>::t;
    

    为了避免链接器错误,您必须为静态变量赋值。

    template<> T<int> T<int>::t = T<int>(); // for example
    

    【讨论】:

    • 是的,引用的声明显然是错误的。您提供的特定解决方案确实有效。更多信息(与 TMI 接壤)添加到原始帖子中。
    猜你喜欢
    • 2018-06-21
    • 2012-07-29
    • 2014-09-23
    • 1970-01-01
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    相关资源
    最近更新 更多