在函数参数中使用 << 运算符

Use << operator inside function arguments

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

类似于:

/* 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 的建议,我正在尝试使用可变参数模板。出于我的目的,我决定创建一个模板,该模板采用参数列表和 returns 组合字符串。

这是代码:

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 '<<' : no operator found which takes a left-hand operand of type 'std::stringstream' (or there is no acceptable conversion) main.cpp 42 Test

错误的行是 getStringInner 中的 ss << first;

我做错了什么?

编辑2:

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

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();
}

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

 foo(a << b);

做一些不同于调用 foo 传递 a 移位 b 的事情(编译器将单独查看 a << b 从而生成位移操作)。

您可以做的是使用预处理器,即可以 foo 通过编写预处理器宏来生成您正在寻找的内容。

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

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

您可以使用宏:

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

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

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

哪个是你想要的。

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

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

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

如果不更改调用站点,您将无法执行此操作。 "The number is: " << 42 只是一个带有 char[] 和一个 int 操作数的移位操作,而不管表达式的结果随后应该传递给其他函数的事实。

如果您愿意稍微更改调用,则可以使用代理 class 重载 operator<< 并将工作委托给 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 << ".");
}

我想到的是创建您自己的字符串 class,例如 Cl_CharArray,并向其添加运算符<<,这将从左边的字符串中创建一个新的 Cl_CharArray 和对了。

可能需要使用模板。这个任务很有趣,我也会在return这里做的。每个 class 然后需要一个函数来将它变成一个字符串(字符数组)。我认为至少一侧(任何运算符的左侧或右侧)必须是 class,所以应该没问题(有一个数字)。