为什么C++ 03需要模板参数来进行外部链接?

Lig*_*ica 14 c++ templates

背景

在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功能的新措辞的间接结果?

Mik*_*son 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 ?!?!?
};
Run Code Online (Sandbox Code Playgroud)