使用静态 class 变量而不分配它们
Using static class variables without allocationg them
背景
所以今天早些时候我正在为 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
和 运行 的值打印成 linking 错误(这是有道理的,我要求的是不在内存中的地址).此外,如果我在 class 定义之后添加 CsvWriter::LineBreak CsvWriter::BREAK;
行,那么我可以毫无问题地打印 &CsvWriter::BREAK
的值(这也很有意义,因为现在我已经给了它内存。
由此,我可以一起疑惑,如果该值从未在任何编译单元中使用过,那么 linker 将永远不会寻找该值,也不会抱怨它。但是,如果我使用它(例如获取地址),linker 将尝试 link 名称,但找不到任何东西。
问题
虽然我发现这个结果对我正在尝试做的事情非常有用,但我很好奇,这在技术上是否违反了 C++11 标准?或者这是完全有效的代码?如果没有,是否有更好的方法使用同样清晰和简单的界面来做到这一点?
从技术上讲,这是一个格式错误的程序。
可能发生的情况是编译器足够聪明,可以看到重载函数的参数值从未被使用过。因此,编译器不会发出将未使用的 BREAK
值传递给重载的 <<
运算符的代码。由于没有生成对符号的引用,因此 linker 不会抱怨。
但是,如果您显式生成对该对象的引用,link当然会失败。
C++ 标准不要求编译器执行此优化,因此这是病式的。所以,从技术上讲,这确实是 C++ 标准所不允许的。
背景
所以今天早些时候我正在为 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
和 运行 的值打印成 linking 错误(这是有道理的,我要求的是不在内存中的地址).此外,如果我在 class 定义之后添加 CsvWriter::LineBreak CsvWriter::BREAK;
行,那么我可以毫无问题地打印 &CsvWriter::BREAK
的值(这也很有意义,因为现在我已经给了它内存。
由此,我可以一起疑惑,如果该值从未在任何编译单元中使用过,那么 linker 将永远不会寻找该值,也不会抱怨它。但是,如果我使用它(例如获取地址),linker 将尝试 link 名称,但找不到任何东西。
问题
虽然我发现这个结果对我正在尝试做的事情非常有用,但我很好奇,这在技术上是否违反了 C++11 标准?或者这是完全有效的代码?如果没有,是否有更好的方法使用同样清晰和简单的界面来做到这一点?
从技术上讲,这是一个格式错误的程序。
可能发生的情况是编译器足够聪明,可以看到重载函数的参数值从未被使用过。因此,编译器不会发出将未使用的 BREAK
值传递给重载的 <<
运算符的代码。由于没有生成对符号的引用,因此 linker 不会抱怨。
但是,如果您显式生成对该对象的引用,link当然会失败。
C++ 标准不要求编译器执行此优化,因此这是病式的。所以,从技术上讲,这确实是 C++ 标准所不允许的。