【问题标题】:Use << operator inside function arguments在函数参数中使用 << 运算符
【发布时间】:2015-03-15 13:26:03
【问题描述】:

我想创建一个函数,允许我在参数中使用&lt;&lt; 运算符,并在其中使用相应的字符串流。

类似:

/* This does not do what i want below */
void printStream(std::stringstream& ss) {
  std::cout << ss.str();
}

/* Desired function usage */
printStream("The number is: " << 42 << ".");

我应该在函数声明中使用什么?

编辑:

按照 Deduplicator 的建议,我正在尝试使用可变参数模板。出于我的目的,我决定创建一个模板,该模板接受参数列表并返回组合字符串。

这是代码:

template <typename First, typename... Rest> void getStringInner(std::stringstream& ss, const First& first, const Rest&... rest) {
  ss << first;
  getStringInner(ss, rest...);
}

template <typename First, typename... Rest> std::string getString(const First& first, const Rest&... rest) {
  std::stringstream ss;
  getStringInner(ss, first, rest...);
  return ss.str();
}

不幸的是,Visual Studio 给了我一个错误:

error C2678: binary '

有错误的行是ss &lt;&lt; first; in getStringInner

我做错了什么?

EDIT2:

我发现了错误。这是工作代码:

template <typename First> void getStringInner(std::stringstream& ss) {}

template <typename First> void getStringInner(std::stringstream& ss, const First& first) {
  ss << first;
}

template <typename First, typename... Rest> void getStringInner(std::stringstream& ss, const First& first, const Rest&... rest) {
  ss << first;
  getStringInner(ss, rest...);
}

template <typename First, typename... Rest> std::string getString(const First& first, const Rest&... rest) {
  std::stringstream ss;
  getStringInner(ss, first, rest...);
  return ss.str();
}

【问题讨论】:

  • 为什么不使用标准的可变参数模板?您将不得不使用逗号,但那又如何?
  • ss 作为const 引用,因为您没有修改它。
  • 也许使用std::ostringstreamFirst是什么类型的?
  • 你确实不应该使用std::stringstream。它支持输入和输出。你只想要输出,所以让std::ostringstream
  • 好的,我会做出改变的。感谢您的建议。

标签: c++ arguments stringstream


【解决方案1】:

不要那样做,它需要预处理器破解。

只需使用可变参数模板和逗号而不是左移:

template<class... X> void printStream(X&&... x) {
    std::stringstream ss;
    ((void)0, (void)(ss<<x), ...);
    std::cout << ss.rdbuf();
}

【讨论】:

  • 或者只是几个 std::to_string 电话和一些 +... :)
  • @ChristianHackl:当然。但这可能会产生更多开销....尽管只有测试实际情况才能说明问题。
  • 对。我想我想表达的观点是,这个问题往往会滋生过度设计的解决方案。
  • 可变参数模板是个好主意。我会检查我是否可以在我的真实代码中使用它。
  • 我确实在我的代码库中使用了一个非常相似的可变参数模板。你可以扔给它任何允许流输出的东西,包括操纵器等。它比普通流输出的优势在于最终只有一个输出操作,避免了多个线程的重叠输出。
【解决方案2】:

您在 C++ 中查找的内容很困难,因为传递给函数的表达式将被独立计算;例如,如果 ab 是整数,则无法编写函数或模板 foo 以便

 foo(a << b);

与调用 foo 并传递 a 并移动 b 不同(编译器将孤立地查看 a &lt;&lt; b 从而生成位移操作)。

您可以做的是使用预处理器,即可以通过编写预处理器宏让foo 生成您要查找的内容。

#define foo(x)                \
   do{                        \
       std::ostream s_;       \
       s_ << x;               \
       std::cout << s_.str(); \
   } while(0)

这项工作利用了通常被认为是 C 预处理器的限制,即它通过文本替换工作。换句话说,编译器会被要求编译s_ &lt;&lt; a &lt;&lt; b

【讨论】:

  • 这是一个很好的打印解决方案,但我的真实代码使用组合字符串来执行更复杂的操作...我需要获取一个可以在记录器中使用的字符串...
  • 我喜欢这个方案,但在工作中不喜欢使用宏,所以我选择了可​​变参数模板方案。
  • @Jepessen:您可以在宏中放置您喜欢的任何代码。在我们的项目中,我们现在使用一些像这样的宏(LOG_INFO、LOG_WARN 和 LOG_ERROR),它们接受类流和 printf 语法,此外还记录源文件和发出命令的行。在某些领域,宏只是优于模板。
【解决方案3】:

你可以使用宏:

#define printStream(arg) std::cout << arg

因此,当您编写 printStream("The number is: " &lt;&lt; 42 &lt;&lt; "."); 时,它将扩展为:

std::cout << "The number is: " << 42 << ".";

你想要什么。

【讨论】:

  • 我认为我们可以放心地假设他的示例中的std::cout 只是一个简化,并且他的真实代码使用了更精细的输出机制(例如,写入一个路径可配置的日志文件运行时)。
  • 实际上是的.. 我有一个记录器,它以字符串作为参数,我不喜欢每次必须使用它来打印包含变量的消息时都创建字符串流......跨度>
  • @Jepessen:为了记录调用,一个好的方法是(1)创建一个类和支持宏来获取源代码位置信息,以及(2)创建一个类似的格式化程序Christian 在他的回答中显示,除了相关的隐式转换为字符串类型。这是关注点分离,为不同的问题创建不同的解决方案。然后,您可以普遍使用这些解决方案,而不仅仅是使用您的记录器。
  • 别忘了这里回答中的愚蠢宏。 :-)
【解决方案4】:

如果不更改呼叫站点,您将无法执行此操作。 "The number is: " &lt;&lt; 42 只是一个带有char[]int 操作数的移位操作,而不管表达式的结果是否应该传递给其他函数。

如果您愿意稍微更改调用,则可以使用重载operator&lt;&lt; 的代理类并将工作委托给std::ostringstream。这是一个基本示例:

#include <iostream>
#include <sstream>
#include <string>

class Printer {
private:
  std::ostringstream oss;

public:

  Printer() {
  }

  std::string string() const {
      return oss.str();
  }

  template <class T>
  Printer& operator<<(T const& t) {
    oss << t;
    return *this;
  }
};

void printStream(Printer const& printer) {
  std::cout << printer.string();
}


int main() {
  printStream(Printer() << "The number is: " << 42 << ".");
}

【讨论】:

  • 我几乎赞成,但后来发现这对被调用的函数提出了要求。那不好。当我在东西方发布这种方法时,/my 格式化程序隐式转换为相关的字符串类型(也支持各种优化,基本上通过调用to_string 函数而不是在内部使用流&lt;&lt;)。
  • @Cheersandhth.-Alf:我碰巧同意你的看法;由于担心过度设计,我自己不会使用这种技术(我会选择std::to_string+,直到实际需要更精细的解决方案,无论是为了性能还是为了可读性)。但我认为无论如何让 OP 了解这种技术是个好主意。这可能是对他或她的编程工具带的有用补充。
【解决方案5】:

我想到的是创建你自己的字符串类,例如Cl_CharArray,并在其中添加运算符Cl_CharArray。

也许需要使用模板。这个任务很有趣,我也会做的,然后回到这里。然后,每个类都需要一个函数将其转换为字符串(char 数组)。我认为至少一侧(任何运算符的左侧或右侧)必须是一个类,所以应该没问题(有一个数字)。

【讨论】:

    猜你喜欢
    • 2021-06-23
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 2020-09-19
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    相关资源
    最近更新 更多