静态模板成员变量具有内部链接但未定义

Posted

技术标签:

【中文标题】静态模板成员变量具有内部链接但未定义【英文标题】:static template member variable has internal linkage but is not defined 【发布时间】:2014-04-23 22:52:42 【问题描述】:

是的,我知道,有一个标题几乎相同的问题,但它指的是不同的情况(给出相同的 clang 错误消息)。就我而言,我有一个 .cpp 文件,其中包含一个很大的匿名命名空间(包含实现细节)。在该命名空间中是一个带有静态数据成员的特征类模板,我需要从匿名命名空间外部访问它。让我稍微充实一下:

file.hpp

namespace bar 
  template<typename A>
  struct foo
  
    static_assert(is_same<A,float>::value || is_same<A,double>::value, "");
    static void set_static_var(A const&x);
    // ...
  ;

file.cpp

namespace 
  template<typename A>
  struct foo_traits
  
    // lots of static code supporting the implementation of bar::foo<>
    static A datum;
  ;
  template<> float  foo_traits<float>::datum;   // no change if this is in global namespace
  template<> double foo_traits<double>::datum;
  template struct foo_traits<float>;
  template struct foo_traits<double>;


namespace bar 
  template<typename A>
  void foo<A>::set_static_var(A const&x)
  
    foo_traits<A>::datum = x;
  
  template struct foo<double>;     // error only appears if these lines are present.
  template struct foo<float>;      // but without these lines, the whole file is void.

我在变量foo_traits&lt;&gt;::datum 上得到了上述错误(以及后来的链接失败)。 在编辑中添加注释我使用clang++ -std=c++11 -stdlib=libc++(3.3 版),它只产生一个警告,但(正如我所说)在我的实际应用程序中支持警告链接失败(正是缺少上述符号)。不过,gcc 没有编译器警告。也许这是一个clang错误? 结束说明

定义这些变量的正确方法是什么?请注意,在 anonymouns 命名空间之外声明它们不会编译。另请注意,AFAIK,具有普通非模板的相同构造也可以工作。

注意我不是在问如何规避这个问题(我自己能想到),而是如何正确地完成。

【问题讨论】:

This (correct) program now compiles without error. @Casey 抱歉,我忘记了最重要的几行,即file.cpp 中的模板实例化。仅当添加这些错误时才会暴露该错误。所以,程序毕竟不正确 Still fine. @Casey 没有警告的编译并不意味着正确。我只收到一个警告clang++ v3.3,gcc v4.9 没有警告),但(在我的实际应用程序中)随后的链接失败。 【参考方案1】:

我认为正确的解决方案很简单:

namespace 
  template<typename A>
  struct foo_traits
  
    // lots of static code supporting the implementation of bar::foo<>
    static A datum;
  ;
  template<typename A> A foo_traits<A>::datum;


您的代码 AFAICT 的问题在于

float foo_traits<float>::datum;

指的是不存在的foo_traits 的特化。 (代码中的template&lt;&gt; 也是不允许的,Clang 也会给出错误)。

见this live example

【讨论】:

@Walter 我不知道您的情况到底是什么问题,因为您没有展示实际产生问题的 SSCCE(这也是凯西想知道的)。我只是展示了我认为正确的解决方案是什么以及通常对我有用的方法(以及现在看来也适用于您的程序的方法 IIUC)。 :) Daniel,上面的代码是 SSCCE,因为clang++ -std=c++11 会发出警告(但不是 gcc 4.9)。 @Walter 用我认为可以解释问题的内容更新了答案。【参考方案2】:

问题是静态数据成员的显式特化声明只有在它包含初始化器时才是定义,否则是声明。 C++11 14.7.3/13:

如果声明包含初始化器,则模板的静态数据成员的显式特化是定义;否则,它是一个声明。 [注意:需要默认初始化的模板的静态数据成员的定义必须使用braced-init-list

template<> X Q<int>::x; // declaration
template<> X Q<int>::x (); // error: declares a function
template<> X Q<int>::x  ; // definition

——尾注]

Clang correctly compiles program if you provide initializers for the specializations:

template<> float  foo_traits<float>::datum = 0;
template<> double foo_traits<double>::datum = 0;

我错误地认为原始程序是正确的,显然是gcc incorrectly treats the declarations as definitions even when they lack initializers。

【讨论】:

+1 这也解释了我的修复工作的原因:它不是专业化的。现在谜题完成了(至少在我的脑海中) @DanielFrey 是的,我所需要的只是反复犯错并与可怜的沃尔特争论。 嗯,初始化会给出“非常量静态数据成员必须被初始化”。

以上是关于静态模板成员变量具有内部链接但未定义的主要内容,如果未能解决你的问题,请参考以下文章

普通内部类不能有静态成员

java基础

java基础学习2

c++类中 各种成员的生命周期?

Java中的静态类

浅谈Java中的内部类