【问题标题】:Why does overloaded function template order matter for fundamental types?为什么重载函数模板顺序对基本类型很重要?
【发布时间】:2018-07-18 22:41:11
【问题描述】:

出于说明目的,假设我有这个人为的容器:

template <typename T>
struct Container {
    T v;
};

而这个人为的“价值:”

struct Value {};

我有一个递归模板函数,声明完全一样:

template <typename T>
void visit(const Container<T> &c) {
    visit(c.v);
}

template <typename T>
void visit(const T &v) {}

根据上述情况,调用visit(Value{}) 是完全有效的。我也可以调用:

void this_works_too(Container<Value> c) {
    visit(c);
}

这有点令人费解,因为递归调用visit(c.v)发生在它的定义之前。

但是,当我们尝试以下操作时,我们得到一个错误(来自 clang 6 和 gcc 8):

void this_blows_up(Container<int> c) {
    visit(c);
}

它现在抱怨递归调用visit(c.v)

error: call to function 'visit' that is neither visible in the
       template definition nor found by argument dependent lookup

但是,如果我们重新排序 visit 的声明:

template <typename T>
void visit(const T &c) {}

template <typename T>
void visit(const Container<T> &c) {
    visit(c.v);
}

this_works_toothis_blows_up 都编译成功。

(此行为也发生在 STL 容器中,与 const 和引用限定符无关)

为什么对于visit(Container&lt;int&gt;) 的专业化来说,订单对visit(Container&lt;Value&gt;) 不重要?

在我的研究中,我怀疑这必须处理ADL(我承认我不完全理解)。但我对此的最佳解释是,在搜索结束时Container&lt;int&gt; 的情况下,由于普通的unqualified lookup,该集合应该找到int。所以,Container&lt;int&gt;Container&lt;Value&gt; 都应该可以工作。

Godbolt demonstrating this

我已经确认,尽管重新排序,但调用了正确的重载(即,当 visit(T) 是第一个时,visit(Container&lt;T&gt;) 实际上是为 visit(Container&lt;int&gt;) 调用的,而不是仅仅选择更通用的 visit(T))。我相信这是因为选择了最具体的专业。

【问题讨论】:

    标签: c++ templates c++17 overloading


    【解决方案1】:

    在我的研究中,我怀疑这必须处理 ADL(我承认我并不完全理解)。

    你说得对,这就是问题所在。

    但我对此的最佳解释是,在 Container&lt;int&gt; 的情况下搜索结束时,由于普通的不合格查找,该集合应该找到 int

    普通的非限定查找看不到 template &lt;typename T&gt; void visit(const T &amp;v) 重载,因为它还没有被声明。依赖于参数的查找是一个例外;它可以在实例化点看到所有声明。

    int 类型是与任何特定命名空间无关的内置类型。因为int 没有任何关联的命名空间,所以ADL 没有在其中搜索声明的命名空间,因此无法找到第二个声明。

    Value 类型是全局命名空间中的用户定义类型。因此,ADL 在全局命名空间中搜索声明,在这种情况下,它确实找到了第二个声明。

    【讨论】:

    • 确实,如果您将Value 放在命名空间中,您会得到完全相同的编译错误:godbolt.org/g/BeShHV
    • 有趣!感谢您对此进行解释。我链接到的页面让我很费解,无法得出像你这样清晰简洁的解释!为了清楚起见,这里的实例化点是函数的调用,对吗? (这就是为什么你不能把所有的声明都放在它们的用法下面?)有什么理由让它这样工作吗?这是否会启用一些原本不可能的行为?这似乎非常违反直觉。
    • @BaileyParker 对于函数模板,很明显需要允许看到以后的声明的东西,但在一般情况下允许这样做是有问题的。我认为这是一种平衡行为,它需要对程序员使用足够有用,但又不能太复杂以至于编译器供应商无法合理地实现它。
    猜你喜欢
    • 2016-04-06
    • 1970-01-01
    • 2014-11-21
    • 2013-05-18
    • 1970-01-01
    • 2017-11-11
    • 2011-11-28
    相关资源
    最近更新 更多