为什么clang要求在模板中调用函数之前先声明一个函数?

东方卫视

我有以下代码示例(可在coliru上在线获得):

#include <iostream>
#include <utility>

struct Bar {
    int a;
};

template <class T>
void print_arg(const T& arg) {
    std::cout << arg << std::endl;    
}

std::ostream& operator<<(std::ostream& os, const Bar& b) {
    os << b.a;
    return os;
}

template <class T1, class T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& pair) {
    os << "Pair(" << pair.first << ',' << pair.second << ")";
    return os;
}

int main()
{
    auto bar = Bar{1};
    print_arg(bar);
    print_arg(std::make_pair(bar, bar));
    print_arg(std::make_pair(bar, 1));
    print_arg(std::make_pair(0, 1));
}

main函数的最后一行是给我带来麻烦的原因。使用g ++进行编译可以很好地工作(具有与下面完全相同的选项),我启动可执行文件并按预期打印所有内容。但是,Clang ++给了我以下错误:

$ clang++ -std=c++17 -O2 -Wall -Werror -Wpedantic main.cpp && ./a.out
main.cpp:10:15: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
    std::cout << arg << std::endl;    
              ^
main.cpp:29:5: note: in instantiation of function template specialization 'print_arg<std::pair<int, int> >' requested here
    print_arg(std::make_pair(0, 1));
    ^
main.cpp:19:15: note: 'operator<<' should be declared prior to the call site
std::ostream& operator<<(std::ostream& os, const std::pair<T, T>& pair) {
              ^
1 error generated.

此外,删除最后一行(注释掉)会导致Clang ++正确编译所有内容。据我所知,这std::pair<int, int>在质量上与其他参数类型不同。

我的问题是,为什么g ++仍要编译它?而且更重要的是,为什么clang认为operator<<(ostream, pair<Bar, Bar>)以后可以声明,但不适合operator<<(ostream, pair<int, int>)是否因为后者仅包括标准和基本类型?

在我看来(某种程度上),逻辑上似乎是仅在标准/基本类型上定义函数是UB,但是g ++默默地忽略了它,而clang ++给出了一个看起来很奇怪的错误消息。但是,这对我来说并没有太大意义,我找不到相关的标准条款。

注意:我知道向上声明是clang要求的,但是我不明白为什么。我想提供print_arg一个单独的头功能,并允许包括头部专门的人operator<<在使用print_arg

'

C ++标准指出,可以通过两种方式查找不合格的名称。根据Clang关于语言兼容性的文档

首先,编译器会在写入名称的范围内进行不限定条件的查找。对于模板,这意味着在定义模板的位置而不是在实例化的位置进行查找。

其次,如果名称像函数一样被调用,则编译器还将执行依赖于参数的查找(ADL)。有时,不合格的查询可以抑制ADL。在ADL中,编译器查看调用的所有参数的类型。当找到一个类类型时,它会在该类的名称空间中查找名称。结果是它在那些命名空间中找到的所有声明,以及来自非限定查找的声明。但是,直到知道所有参数类型,编译器才执行ADL。

有两种方法可以解决此问题:

  1. 确保在要调用的函数之前声明了要调用的函数。如果其所有参数类型都不包含类,则这是唯一的选择。您可以通过移动模板定义,移动函数定义或在模板之前添加函数的前向声明来执行此操作。
  2. 将函数移至与其参数之一相同的名称空间中,以便应用ADL。

↳参见basic.lookup.argdeptemp.dep.candidate

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在定义它之前在Swift中声明一个函数

来自分类Dev

为什么要声明/创建一个单独的变量以通过引用进行调用,而不只是在函数调用中创建一个自动对象?

来自分类Dev

为什么必须在使用每个函数的每个函数之前声明一个模板?

来自分类Dev

为什么在声明成员函数之前先对其进行调用?

来自分类Dev

在React中,为什么我必须绑定一个onClick函数而不是调用它?

来自分类Dev

在定义重载之前先声明功能模板

来自分类Dev

为什么要在C中声明一个函数?

来自分类Dev

为什么在调用函数之前先运行它们?

来自分类Dev

为什么在函数声明中的参数名称之前有一个类名称?

来自分类Dev

为什么我必须从另一个函数中调用一个函数?

来自分类Dev

为什么不从另一个函数中调用一个函数而不是使用回调呢?

来自分类Dev

为什么在装饰器被调用之前必须先声明它们,而函数却不必声明呢?

来自分类Dev

如果不包含标题,为什么在调用函数之前先清除EAX?

来自分类Dev

在使用函数之前先声明它是否有所不同?

来自分类Dev

为什么使用模拟类中另一个函数调用的函数的测试失败?

来自分类Dev

为什么要在对象的函数属性中声明变量之前先引用它?

来自分类Dev

为什么要在源文件中定义一个函数,然后在使用其引用之前在另一个文件中对其进行声明?

来自分类Dev

为什么需要定义一个函数才能将其传递给不调用它的模板函数?

来自分类Dev

为什么即使没有在C中首先声明int函数也可以工作

来自分类Dev

为什么在调用F#函数之前先对其求值?

来自分类Dev

为什么不从另一个函数中调用一个函数而不是使用回调呢?

来自分类Dev

为什么在装饰器被调用之前必须先声明它们,而函数却不必声明呢?

来自分类Dev

为什么要编译?在另一个函数中声明的函数

来自分类Dev

点击后。一个函数被多次调用......为什么?

来自分类Dev

为什么订阅处理程序函数在第一个函数之前的行为与 onValue 不同?

来自分类Dev

ajax beforeSend 之前先解析一个函数

来自分类Dev

为什么类模板中的成员函数在同一个地址创建同一个对象

来自分类Dev

为什么我不能在 Chrome 中声明一个与函数体同名的 let 变量

来自分类Dev

如何从之前定义的另一个函数调用函数模板?

Related 相关文章

  1. 1

    在定义它之前在Swift中声明一个函数

  2. 2

    为什么要声明/创建一个单独的变量以通过引用进行调用,而不只是在函数调用中创建一个自动对象?

  3. 3

    为什么必须在使用每个函数的每个函数之前声明一个模板?

  4. 4

    为什么在声明成员函数之前先对其进行调用?

  5. 5

    在React中,为什么我必须绑定一个onClick函数而不是调用它?

  6. 6

    在定义重载之前先声明功能模板

  7. 7

    为什么要在C中声明一个函数?

  8. 8

    为什么在调用函数之前先运行它们?

  9. 9

    为什么在函数声明中的参数名称之前有一个类名称?

  10. 10

    为什么我必须从另一个函数中调用一个函数?

  11. 11

    为什么不从另一个函数中调用一个函数而不是使用回调呢?

  12. 12

    为什么在装饰器被调用之前必须先声明它们,而函数却不必声明呢?

  13. 13

    如果不包含标题,为什么在调用函数之前先清除EAX?

  14. 14

    在使用函数之前先声明它是否有所不同?

  15. 15

    为什么使用模拟类中另一个函数调用的函数的测试失败?

  16. 16

    为什么要在对象的函数属性中声明变量之前先引用它?

  17. 17

    为什么要在源文件中定义一个函数,然后在使用其引用之前在另一个文件中对其进行声明?

  18. 18

    为什么需要定义一个函数才能将其传递给不调用它的模板函数?

  19. 19

    为什么即使没有在C中首先声明int函数也可以工作

  20. 20

    为什么在调用F#函数之前先对其求值?

  21. 21

    为什么不从另一个函数中调用一个函数而不是使用回调呢?

  22. 22

    为什么在装饰器被调用之前必须先声明它们,而函数却不必声明呢?

  23. 23

    为什么要编译?在另一个函数中声明的函数

  24. 24

    点击后。一个函数被多次调用......为什么?

  25. 25

    为什么订阅处理程序函数在第一个函数之前的行为与 onValue 不同?

  26. 26

    ajax beforeSend 之前先解析一个函数

  27. 27

    为什么类模板中的成员函数在同一个地址创建同一个对象

  28. 28

    为什么我不能在 Chrome 中声明一个与函数体同名的 let 变量

  29. 29

    如何从之前定义的另一个函数调用函数模板?

热门标签

归档