14

背景

在 C++03 中,用作模板参数的符号必须具有外部链接;如上一个问题所述,限制在 C++11 中已删除:

在 C++03 中,模板参数不能有内部链接:

[C++03: 14.6.4.2/1]:对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则(3.4.1、3.4.2)找到候选函数,但以下情况除外:

  • 对于使用非限定名称查找(3.4.1) 的查找部分,只能找到具有来自模板定义上下文的外部链接的函数声明
  • 对于使用关联命名空间 (3.4.2) 的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明。

[..]

这在 C++11 中已更改(问题#561:“依赖名称查找中的内部链接函数” ):

[C++11: C.2.6]:14.6.4.2
更改:允许具有内部链接的函数的依赖调用
理由:过度约束,简化重载决议规则。

导致:

[C++11: 14.6.4.2/1]:对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1、3.4.2、3.4.3)找到候选函数,除了:

  • 对于使用非限定名称查找(3.4.1) 或限定名称查找 (3.4.3) 的查找部分,只能找到来自模板定义上下文的函数声明。
  • 对于使用关联命名空间 (3.4.2) 的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明。

[..]

(发现缺少的“与外部联系”资格。)

问题 #561(“依赖名称查找中的内部链接函数”),导致在 C++11 中删除限制的提议,要求:

此外,真的有必要从查找中排除内部链接功能吗?ODR 是否为实现提供了足够的自由度来处理这种情况,而不会在名称查找上出现另一个问题?

随着后来的答案:

该小组的共识是[..]内部链接函数应该通过查找找到(尽管如果通过重载决议选择它们可能会导致错误)。


问题

限制的最初实际理由是什么?

似乎必须有一个,因为最初的标准措辞已将查找限制为具有外部链接的符号。

仅仅是“[内部链接功能]如果通过重载解决方案选择可能会导致错误”,并且通过 2000 年代的观点转移了这有多重要?还是其他地方发生了变化,也许是其他地方针对不同 C++11 特性的新措辞的间接后果?

4

2 回答 2

8

我怀疑它与exportC++98 臭名昭著的模板特性有关。想想看。一旦允许模板定义出现在单独的翻译单元中的可能性,但在指定模板参数之前仍然不能真正编译(即,模板被实例化),您就进入了这个暮光区,其中带有模板定义的 TU 和具有实例化的 TU 必须遵守链接器可见性规则(即分离模型),同时在过载解决方面共享它们的上下文。该问题的解决方案是在从属名称查找中只允许具有外部链接的函数。

这是一个例子。导出模板的鲜为人知的“功能”之一是,您可以在模板的 TU 中拥有一些具有内部链接的函数或类(即,标记static或在未命名的命名空间中)。如果具有实例化的 TU 也具有内部链接功能,该功能会与模板 TU 中的功能不明确或可能被取代怎么办?这是一个有点超现实的问题,我知道,这是导出模板的奇异世界。避免非常令人惊讶的行为的唯一方法是从查找中排除所有内部链接函数。还要考虑到没有人清楚地知道如何实际实现导出的模板,如果没有这个限制,实现起来可能会更加不可能。

因此,一旦导出的模板出来了,对依赖名称查找的限制似乎显然没有用,并且在没有太多争论的情况下就被取消了。至少,这对我来说很有意义,但是,当然,这是猜测。

这是一个具体的例子:

// in exptemp.h
export template <typename T> bool is_valid(T value);

// in exptemp.cpp
namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t');
  };
};

template <typename T>
bool is_valid(T value) {
  return is_space(value);
};

// in test.cpp
#include "exptemp.h"

namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t') || (c == '\n');
  };
};

int main() {
  char c = '\n';
  return is_valid(c);   // will this return 0 or 1 ?!?!?
};
于 2013-01-30T06:51:45.177 回答
3

据我所知,这纯粹是历史性的——它似乎最初被禁止了,因为 cfront 的名称修改不足以正确处理它。

有一次,Anthony Williams 写了一篇论文,提议允许这样做,并告诉如何去做——但是 AFAIK,那篇论文从未被接受,其要求也从未被编辑到标准中。我怀疑这和其他任何事情一样都是时间问题。它是在 2001 年提出的,所以他们当时的工作(C++ 2003)并没有打算添加太多新材料,当他们开始认真研究 C++11 时,似乎主要是忘记了。

于 2013-01-30T06:24:50.367 回答