开发者

Scope quandary with namespaces, function templates, and static data

开发者 https://www.devze.com 2022-12-28 16:10 出处:网络
This scoping problem seems like the type of C++ quandary that Scott Meyers would have addressed in one of his Effective C++ books.

This scoping problem seems like the type of C++ quandary that Scott Meyers would have addressed in one of his Effective C++ books.

I have a function, Analyze, that does some analysis on a range of data. The function is called from a few places with different types of iterators, so I have made it a template (and thus implemented it in a header file). The function depends on a static table of data, AnalysisTable, that I don't want to expose to the rest of the code.

My first approach was to make the table a static const inside Analysis.

namespace MyNamespace {

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

}  // namespace MyNamespace

It appears that the compiler creates a copy of AnalysisTable for each instantiation of Analyze, which is wasteful of space (and, to a small degree, time).

So I moved the table outside the function like this:

namespace MyNamespace {

  const int AnalysisTable[] = { /* data */ };

  template <typename InputIterator>
  int Analyze(InputIterator begin, InputIterator end) {
    ... // implementation uses AnalysisTable
    return result;
  }

}  // namespace MyNamespace

There's only one copy of the table now, but it's exposed to the rest of the code. I'd rather keep this implementation detail hidden, so I introduced an unnamed namespace:

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

But now I again have multiple copies of the table, because each compilation unit that includes this header file gets its own. If Analyze weren't a template, I could move all the implementation detail out of the header file. But it is a template, so I seem stuck.

My next attempt was to put the table in the implementation file and to make an extern declaration within Analyze.

// 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 */ };
}

This looks like it should work, and--indeed--the compiler is satisfied. The linker, however, complains, "unresolved external symbol AnalysisTable." Drat! (Can someone explain what I'm missing here?)

The only thing I could think of was to give the inner namespace a name, declare the table in the header, and provide the actual data in an implementation file:

// 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 */ };
  }
}

Once again, I have exactly one instance of AnalysisTable (yay!), but other parts of the program can access it (boo!). The inner namespace makes it a little clearer that they shouldn't, but it's still possible.

Is it possible to have one instance of the table and to move the table beyond the reach of everything but Analyze?


You could use a class with a static member to hold the data if it's really important that nothing but specializations of your function template can access the data.

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];
    // ...
}
0

精彩评论

暂无评论...
验证码 换一张
取 消