代码之家  ›  专栏  ›  技术社区  ›  Adrian McCarthy

名称空间、函数模板和静态数据的作用域窘境

  •  3
  • Adrian McCarthy  · 技术社区  · 14 年前

    有效C++

    Analyze ,对一系列数据进行分析。这个函数是用不同类型的迭代器从几个地方调用的,所以我把它做成了一个模板(并在头文件中实现了它)。函数依赖于静态数据表, AnalysisTable ,我不想暴露给代码的其余部分。

    我的第一个方法是把桌子做成一张桌子 static const 里面 Analysis

    namespace MyNamespace {
    
      template <typename InputIterator>
      int Analyze(InputIterator begin, InputIterator end) {
        static const int AnalysisTable[] = { /* data */ };
        ... // implementation uses AnalysisTable
        return result;
      }
    
    }  // namespace MyNamespace
    

    编译器似乎创建了 分析表 对于的每个实例化 分析 ,这是浪费空间(在小的程度上,也是浪费时间)。

    所以我把表移到函数外,像这样:

    namespace MyNamespace {
    
      const int AnalysisTable[] = { /* data */ };
    
      template <typename InputIterator>
      int Analyze(InputIterator begin, InputIterator end) {
        ... // implementation uses AnalysisTable
        return result;
      }
    
    }  // namespace MyNamespace
    

    现在只有一个表的副本,但它暴露在其余代码中。我宁愿隐藏这个实现细节,所以我引入了一个未命名的命名空间:

    namespace MyNamespace {
    
      namespace {  // unnamed to hide AnalysisTable
        const int AnalysisTable[] = { /* data */ };
      }  // unnamed namespace
    
      template <typename InputIterator>
      int Analyze(InputIterator begin, InputIterator end) {
        ... // implementation uses AnalysisTable
        return result;
      }
    
    }  // namespace MyNamespace
    

    但是现在我又有了这个表的多个副本,因为包含这个头文件的每个编译单元都有自己的副本。如果 如果不是模板,我可以将所有实现细节从头文件中移出。但它是一个模板,所以我似乎卡住了。

    我的下一个尝试是将表放在实现文件中并 extern 内声明 .

    // foo.h ------
    namespace MyNamespace {
    
      template <typename InputIterator>
      int Analyze(InputIterator begin, InputIterator end) {
        extern const int AnalysisTable[];
        ... // implementation uses AnalysisTable
        return result;
      }
    
    }  // namespace MyNamespace
    
    // foo.cpp ------
    #include "foo.h"
    namespace MyNamespace {
        const int AnalysisTable[] = { /* data */ };
    }
    

    这看起来应该是可行的,而且——事实上——编译器很满意。然而,链接器抱怨说,“未解析的外部符号

    我唯一能想到的是给内部命名空间起一个名字,在头中声明表,并在实现文件中提供实际数据:

    // foo.h -----
    namespace MyNamespace {
    
      namespace PrivateStuff {
        extern const int AnalysisTable[];
      }  // unnamed namespace
    
      template <typename InputIterator>
      int Analyze(InputIterator begin, InputIterator end) {
        ... // implementation uses PrivateStuff::AnalysisTable
        return result;
      }
    
    }  // namespace MyNamespace
    
    // foo.cpp -----
    #include "foo.h"
    namespace MyNamespace {
      namespace PrivateStuff {
        const int AnalysisTable[] = { /* data */ };
      }
    }
    

    再一次,我正好有一个 分析表 (耶!),但是程序的其他部分可以访问它(boo!)。内部名称空间使得它们 不应该 ,但还是有可能的。

    有没有可能有一个表的实例,并将表移动到除了 分析 ?

    1 回复  |  直到 14 年前
        1
  •  3
  •   CB Bailey    14 年前

    class AnalysisTable
    {
        static const int data[];
    
        template <typename InputIterator>
        friend int Analyze(InputIterator begin, InputIterator end);
    };
    
    template <typename InputIterator>
    int Analyze(InputIterator begin, InputIterator end)
    {
        // ...
        x = AnalysisTable::data[n];
        // ...
    }