为什么我们不应该对那些不接受C++论据的操纵者使用括号呢?

Why shouldn't we use parenthesis for manipulators that don't take arguments in C++?

本文关键字:操纵者 不应该 我们 那些 不接受 C++ 为什么      更新时间:2023-10-16

例如,setw接受一个整数参数,这意味着我们可以写setw(5),而leftright操纵符不接受任何参数。

  1. 为什么用left()right()代替leftright是错误的?

  2. 带实参的操纵符和不带实参的操纵符有什么区别?

为什么使用left()和right()而不是left和right是错误的?

因为它无法编译!

它还会增加额外的函数调用,而没有任何好处。如果left被作为一个函数调用,那么它必须返回一些与left相同的目的,这将需要定义一个额外的类型和operator<<的额外重载来处理该类型。何苦呢?

带实参的操作符和不带实参的操作符有什么区别?

接受参数的操作符是有状态的,它们返回一些存储参数的对象,然后使用这些参数来改变流状态,例如,对于每个接受参数的操作符,您有这样的内容:

struct SetW { int w; };
SetW
setw(int w) { return SetW{w}; }
ostream&
operator<<(ostream& out, SetW sw)
{
  out.width(sw.w);
  return out;
}

每个操纵符都有一个类似SetW的类型,必须是这些类型中的每个类型的重载。

但是对于没有实参的操纵符,它们的定义(大致)如下:

ostream&
left(ostream& out)
{
  out.str.setf(ios_base::left, ios_base::adjustfield).
  return out;
}

,并且有一个重载处理所有这种形式的操作符,通过接受指向函数的指针作为实参:

using zero_arg_manipulator_type = ostream& (*)(ostream&);
ostream&
operator<<(ostream& out, zero_arg_manipulator_type f)
{
  f(out);
  return out;
}

这不需要像SetW那样的额外类型,并且只需要一个重载来处理所有的零参数操纵符,因此更简单,代码更少。

如果您必须调用left(),那么它需要返回一些自定义类型,如SetW,因此必须为每个操纵符定义另一种类型,并且必须有更多的重载。这将使库复杂化,使其编译速度变慢,并可能使可执行文件变大变慢。

(为了回答这个问题,上面的细节被简化了,实际上函数是使用basic_ostream而不是ostream的模板,一些操纵符使用ios_basebasic_ios而不是basic_ostream ',但这并没有改变它如何工作的总体思想)。

这将是错误的,因为它不会工作:)-标准库是这样设计的,left()将无法工作。

'无参数操纵符'只是函数(你可以自己调用它们,比如std::left(stream))(见这里)

有一个重载的operator<<,它接受这些函数作为参数,允许编写out << std::left,它将生成与std::left(out)相同的代码。out << std::left只是一个方便的符号。这也意味着您可以轻松地创建自己的操纵符。

在传递实际参数时,需要更复杂的代码,例如(不是实际示例,只是解释):

struct setw_t { int width; };
inline setw_t setw(int n) { return setw_t{n}; }
inline std::ostream& operator<<(std::ostream& out, setw_t t)
{
  out.width(t.width);
  return out;
}
因此,调用setw(5)将产生更复杂的代码,但这并不奇怪,因为必须以某种方式传递5

允许写std::left()(例如为了一致性)将需要创建许多这样的"标签结构",并且只会使添加新结构变得复杂。