为什么模板运算符<<不能推断出std::endl?
为什么模板运算符<<不能推断出std::endl?
如果取消注释第一个操作符定义,这段代码将能够编译和运行:\n
#includestruct logger { std::ostream &loggingStream; logger(std::ostream &ls) : loggingStream(ls) {} }; /* logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) { manip(l.loggingStream); return l; } */ template logger &operator<<(logger &l, const T &t) { l.loggingStream << t; return l; } int main() { logger l(std::cout); l << "Hello" << std::endl; return 0; }
\n加上注释后会出现以下错误:\n
error: no match for ‘operator<<’ (operand types are ‘logger’ and ‘’)
\n为什么我需要提供一个非模板重载来处理 `endl`?
问题是,为什么模板操作符<<不能推断出std::endl的类型,以及解决方法。
原因是,作为一个函数模板,std::endl是一个重载集,涉及到模板参数推断;而模板参数推断不能在重载集上工作(除非重载集只包含一个函数)。
为了说明这一点,考虑下面的例子:
templatevoid functor(Function f) { f(0); } void g(float) {} void g(double) {} functor(g);
在这个例子中,没有理由偏好其中一个版本的g,除非你明确地特化functor(functor
如果g是一个模板,情况也是如此:http://coliru.stacked-crooked.com/a/8e27a45bbeedd979
解决方法是,在使用模板操作符<<时,显式指定std::endl的类型,例如:
std::cout << std::endl>;
或者使用具体的类型而不是模板,例如:
std::cout << '\n';
为什么模板操作符<<不能推断std::endl?
在C++中,std::endl是一个模板。当我们有第一个重载的时候,它的参数可以通过匹配函数指针来推断。因为这是类型自动推导的一种情况。
但是,在只有operator<<模板的情况下,有什么可以推断的呢?这两个模板都需要推断它们的参数。
为了更好地理解这个问题,让我们先来看一下C++中的模板和类型自动推导。
模板是一种通用的代码模式,它允许在不指定具体类型的情况下编写函数或类。它们允许我们写出适用于多种类型的代码,从而提高代码的重用性和灵活性。
类型自动推导(Type Argument Deduction,TAD)是C++中的一种特性,它允许编译器根据函数调用的参数来推断模板函数的模板参数类型。这使得我们可以在调用函数时省略模板参数类型,从而简化代码。
现在让我们回到我们的问题上来。为什么模板操作符<<不能推断std::endl的参数类型?
首先,让我们看一下std::endl的定义:
template
std::basic_ostream
从上面的定义可以看出,std::endl是一个函数模板,它接受一个std::basic_ostream对象的引用并返回该对象的引用。然而,在只有operator<<模板的情况下,编译器无法根据这个定义来推断std::endl的参数类型。
为了解决这个问题,我们可以将std::endl的参数类型指定为模板参数,然后将其传递给operator<<模板。这样,编译器就可以根据传递给operator<<的参数类型来推断std::endl的参数类型。
下面是一个示例代码:
// 模板操作符<< templatestd::ostream& operator<<(std::ostream& os, const T& obj) { os << obj; return os; } // 使用模板操作符<< template void print(const T& obj) { std::cout << obj << std::endl; } int main() { int num = 42; print(num); return 0; }
在上面的示例代码中,我们将std::endl的参数类型指定为模板参数T,并将其传递给operator<<模板。这样,编译器就能够根据传递给print函数的参数类型来推断std::endl的参数类型。
总结一下,模板操作符<<无法推断std::endl的参数类型是因为它们都是模板,需要根据上下文来推断参数类型。为了解决这个问题,我们可以将std::endl的参数类型指定为模板参数,并将其传递给operator<<模板。这样,编译器就可以根据上下文来推断参数类型。