使用静态类变量而不分配它们

时间:2016-10-30 00:58:08

标签: c++ c++11 static-variables

背景

今天早些时候我正在为std::ofstream实现一个瘦包装器,它允许我轻松地写入.csv文件。我想覆盖<<运算符以写入值后跟逗号,然后当新行的时间到来时,我会打印一个退格符,然后是一个新行。我决定将新行行为实现为模板专业化,如下所示:

// *this << value; => prints the value to the csv file
// *this << CsvWriter::BREAK; => goes to the next line of the csv file
// Example:
//   CsvWriter csv("test.csv", {"Col 1", "Col 2", "Col 3"});
//   csv << "Value 1" << 2 << 3.25 << CsvWriter::BREAK;
class CsvWriter {
 public:
  CsvWriter(const char *fname, std::vector<const char *> headers);
  CsvWriter() = delete;
  CsvWriter(const CsvWriter &) = delete;
  CsvWriter &operator=(const CsvWriter &) = delete;
  ~CsvWriter() = default;

  // Used for the template specialization below
  static struct LineBreak {} BREAK;

  template <typename T>
  CsvWriter &operator<<(T t);

 private:
  std::ofstream out_;
  bool first_ = true;  // true when printing first element of line
};

template <typename T>
CsvWriter &CsvWriter::operator<<(T t) {
  if (!first_) {
    out_ << ',';
  } else {
    first_ = false;
  }
  out_ << t;
  return *this;
}

// This is the specialization for newlines.
// If anything of type LineBreak is passed to this function, this executes.
template <>
CsvWriter &CsvWriter::operator<<(LineBreak) {
  out_ << std::endl;
  first_ = true;  // Reset first for next line
  return *this;
}

// The constructor gives an example use of the template specialization
CsvWriter::CsvWriter(const char *fname, std::vector<const char *> headers)
    : out_(fname) {
  for (const char *header : headers) {
    *this << header;  // Prints each string in header
  }
  *this << BREAK;  // Goes to the next line of the csv
}

简要说明

此代码完美无缺,并且在gcc中没有任何投诉。但是,我注意到我在技术上没有为值BREAK分配内存。因此,为了检查它,我尝试打印&CsvWriter::BREAK的值并遇到链接错误(这是有道理的,我要求的内容不在内存中)。而且,如果我在类定义之后添加行CsvWriter::LineBreak CsvWriter::BREAK;,那么我可以打印&CsvWriter::BREAK的值没有问题(这也是有道理的,因为现在我已经给它了记忆。

从这一点来说,我可以一起思考如果从未在任何编译单元中使用该值,链接器将永远不会查找该值并且永远不会抱怨它。但是如果我使用它(例如抓取地址),链接器将尝试链接名称,而不是找到任何东西。

问题

虽然我发现这个结果对我正在尝试的内容非常有用,但我很好奇,这在技术上是否符合C ++ 11标准?或者这是完全有效的代码?如果没有,是否有更好的方法来使用类似的清晰简单的界面?

1 个答案:

答案 0 :(得分:0)

这在技术上是一个形成不良的计划。

可能发生的事情是编译器足够聪明,可以看到永远不会使用重载函数的参数值。因此,编译器不会发出代码以将未使用的BREAK值传递给重载的<<运算符。由于没有生成对符号的引用,链接器不会抱怨。

但是如果你明确地生成了对象的引用,那么链接当然会失败。

C ++标准不要求编译器执行此优化,因此这是不正确的。因此,从技术上讲,C ++标准确实不允许这样做。