【问题标题】:Two-phase function template compilation: not *only* ADL is employed in the 2nd phase?两阶段函数模板编译:不是*仅* ADL 在第二阶段使用?
【发布时间】:2019-11-10 21:28:56
【问题描述】:

我想知道为什么 following code 会编译。

#include <iostream>

template<class T> 
void print(T t) {
    std::cout << t;
}

namespace ns {
    struct A {};
}

std::ostream& operator<<(std::ostream& out, ns::A) {
    return out << "hi!";
}

int main() {
    print(ns::A{}); 
}

我的印象是,在实例化点,不合格的依赖名称只能通过 ADL 进行查找 - 这不应该考虑全局命名空间。我错了吗?

【问题讨论】:

  • Clang 拒绝编译这个,正如你所料:error: call to function 'operator&lt;&lt;' that is neither visible in the template definition nor found by argument-dependent lookup。可能是 GCC 错误?
  • @HolyBlackCat 可能甚至不是一个错误,因为几乎任何带有模板的错误都是 UB(可悲)。
  • @curiousguy UB 到底是什么?
  • @IgorR。任何时候在模板中查找在不同点找到另一个名称并重载选择它时,它就是 UB。我想。情况很复杂。太复杂了!

标签: c++ templates language-lawyer argument-dependent-lookup name-lookup


【解决方案1】:

这是一个有趣的案例。您描述的名称查找的工作原理总结如下:

[temp.dep.candidate](强调我的)

1 对于 后缀表达式 是依赖项的函数调用 名称,候选函数是使用通常的查找规则找到的 ([basic.lookup.unqual], [basic.lookup.argdep]) 除了:

  • 对于使用非限定名称查找的查找部分,只能找到来自模板定义上下文的函数声明。

  • 对于使用关联命名空间 ([basic.lookup.argdep]) 的查找部分,仅在两者中找到的函数声明 模板定义上下文或模板实例化上下文 找到了。

如果调用格式不正确或找到更好的匹配项 在关联的命名空间中查找考虑了所有功能 在这些名称空间中引入的具有外部链接的声明 所有翻译单元,而不仅仅是考虑找到的那些声明 在模板定义和模板实例化上下文中,然后 程序有未定义的行为。

我强调的一点是问题的症结所在。 “ADL only”的描述是针对来自foo(bar)的函数调用!它没有提到重载运算符导致的调用。我们知道调用重载运算符等同于调用函数,但该段落谈到了特定形式的表达式,仅是函数调用。

如果要将您的函数模板更改为

template<class T> 
void print(T t) {
    return operator<< (std::cout, t);
}

现在通过后缀表达式表示法调用函数,然后你看:GCC emits an equivalent error to Clang。它可靠地实现了上述段落,只是在涉及重载运算符调用时无法实现。

这是一个错误吗?我会说是的。这样做的目的肯定是像命名函数一样找到重载的运算符(即使从它们各自的表达式形式调用时也是如此)。所以GCC需要修复。但该标准也可以对措辞进行细微的澄清。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多