为什么函数指针不能被通用引用所捕获?

12

在实现一个简单的日志记录器时

struct DebugOutput {
    DebugOutput(std::ostream& out = std::cerr) : m_Out(out) {}

    template<typename T>
    inline DebugOutput& operator <<(T&& value) {
        m_Out << value;
        return *this;
    }
private:
    std::ostream& m_Out;
};

我发现 std::endl 不会被 通用引用 捕获。

DebugOutput dbg;
dgb << std::endl;

我发现了这篇文章,解释说你需要在结构体内添加一个重载函数,它专门接收函数指针的签名,例如:

typedef std::ostream& (*StandardEndLine)(std::ostream&);
inline DebugOutput& operator<<(StandardEndLine manip) {
    return *this;
}

为什么通用引用不能捕获函数指针?难道它不是像intvoid*一样的类型吗?


这是一个模板/重载。 - Xeo
3
https://dev59.com/XnNA5IYBdhLWcg3wAItP#1136617 - R. Martinho Fernandes
可能是重复的问题:在重载operator<<时,std::endl的类型未知 - Xeo
谢谢@R.MartinhoFernandes,我错过了这个解释得非常清楚。 - Guillaume Chatelet
1个回答

13

一个函数(指针)可以绑定到通用引用上。例如:

void f(int) {}

template <typename T>
void foo(T&&) {}

foo(f); // OK

然而,一个过载的函数则不能。也就是说,如果你添加第二个重载的f,比如说,

void f(double) {}

调用foo(f)将失败。

把自己放在编译器的位置上。需要将f传递给foo,而有两个名为f的函数,每个函数都具有不同的类型。如果我们告知类型,那么编译器就可以明确地选择正确的f。例如,

foo(static_cast<void (*)(int)>(f));

编译正常,将 void f(int)(经过函数转指针)传递给 foo

然而,我们没有指定类型,而是要求编译器推断出类型。

f 相似,同样的参数适用于 std::endl,因为这是一个函数模板,因此名称 std::endl 表示一组函数,所有函数名称相同但类型不同。

现在,您可以看到错误的原因是我们提供了一个重载集并要求进行类型推断。因此,这不仅适用于通用引用。

std::cout << std::endl 可以工作是因为 basic_ostream::operator << 不是模板,也不尝试推断传递参数的类型。它是一个接受特定类型的 std::endl 的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接