模板方法何时可以使用稍后定义的函数,而无需前向声明?

Posted

技术标签:

【中文标题】模板方法何时可以使用稍后定义的函数,而无需前向声明?【英文标题】:When can a template method use a function defined later, without forward declaration? 【发布时间】:2014-07-26 04:47:02 【问题描述】:

我正在编写一个类似于 QDebug 的简单记录器类,它具有将数据保存到 QStringList 的模板方法。代码在这里:

#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>

class Logger

    public:
        Logger();

        ~Logger();

        template <typename V>
        Logger &operator<<(V const &value);

    private:
        QStringList msg;
;

inline Logger::Logger():
    msg(QString("INFO:"))


inline Logger::~Logger()

    QTextStream out(stderr);
    out << msg.join("");


template <typename V>
inline Logger &Logger::operator<<(V const &value)

    msg << log(value);
    return *this;


inline QString log(QString const &value)

    return value;


inline QString log(int const (&value)[20])

    return QString("Array");


int main(int argc, char *argv[])

    Logger c;
    int a[20] = ;
    c << QString("test") << a;

    return 0;

但是,这不能与 GCC 4.8.3 一起编译。

$ g++ -I/usr/include/qt4 -L/usr/lib64/qt4 -lQtCore -o test2 test2.cpp
test2.cpp: In instantiation of ‘Logger& Logger::operator<<(const V&) [with V = int [20]]’:
test2.cpp:50:29:   required from here
test2.cpp:32:21: error: ‘log’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
     msg << log(value);
                     ^
test2.cpp:41:16: note: ‘QString log(const int (&)[20])’ declared here, later in the translation unit
 inline QString log(int const (&value)[20])

确实,如果我将inline QString log(int const (&amp;value)[20]) 移到开头或提出前向声明,它就会编译并工作。但让我困惑的是inline QString log(QString const &amp;value) 没有任何问题:

$ ./test2
INFO:testArray

我注意到 QHash 依赖于 qHash 函数,在这种情况下也是类似的。 QHash 与用户定义的键类配合得很好(数组除外,它不能是函数返回类型)。

为什么他们的行为不同?我在这里错过了什么?

感谢您的帮助。

顺便说一句:你能告诉我什么是这个问题的好关键字。我尝试过“专业化”“模板”“前向声明”“QHash”和“用户定义类型”的组合,但它们不起作用。

【问题讨论】:

我不清楚你所说的“内联 QString log(QString const &value) 没有任何问题”是什么意思。此代码中没有调用 log 的重载。 累得想不开,不过大概和cppquiz.org/quiz/question/130有关 @chris,谢谢你的测验。请纠正我:msg &lt;&lt; log(value) 是依赖的,知道类型后会推迟。并且该类型在 c &lt;&lt; QString("test") &lt;&lt; a; 已知,它在 inline QString log(int const (&amp;value)[20]) 的定义之后。 @Matt McNabb:我只是添加了工作程序的结果。因为它输出test,它来自QString("test"),我认为它实际上是被调用的。 @nocte107 工作程序的代码在哪里? 【参考方案1】:

名称log 被查找了两次。在模板定义时,执行普通查找。它没有找到任何东西,因为此时未声明 log

然后,在实例化点,仅执行与参数相关的查找。当参数为QString 类型时,会搜索全局命名空间,因为在那里声明了QString,因此找到了log(QString)。但是int[] 类型没有任何关联的命名空间,因此依赖于参数的查找没有可搜索的内容,也找不到任何结果。因此出现错误。

【讨论】:

谢谢。这是否意味着,如果我想使用这个类,我已经为所有没有关联命名空间的基本类型定义了log?我可以枚举所有这些类型吗? 好吧,您必须为您计划调用的每种类型定义log。参数是基本类型还是其他类型仅决定您在使用前是否需要声明(或定义) - 但无论哪种方式,您都需要在 somewhere 进行定义。 @IgorTandetnic:我明白这一点。对于自定义类型,我可以有一个轻松的位置来定义。对于基本类型,我需要在模板之前定义。我检查了 msdn.microsoft.com/en-us/library/cc953fe1.aspx ,并注意到它实际上并没有那么多类型。但是与数组等结合起来真的很烦人。也许我应该为该日志记录功能编写一个模板类。再次感谢。

以上是关于模板方法何时可以使用稍后定义的函数,而无需前向声明?的主要内容,如果未能解决你的问题,请参考以下文章

前向声明/何时最好包含标题?

没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译

我需要相互包含两个头文件,而不是使用前向声明导致“不完整类型”错误

何时使用函数模板而不是通用 lambda?

Azure 函数 - 我们何时需要使用 out 进行输出绑定?

关于声明定义前向声明include循环依赖普通友元函数友元类友元成员函数的总结