【问题标题】:c++ How does compiler know that a parameter is an STL container?c++ 编译器如何知道一个参数是一个 STL 容器?
【发布时间】:2014-11-03 15:51:45
【问题描述】:

c++ 新手问题 - C++ 编译器如何知道模板函数的参数有 STL 方法作为成员?在 C# 中,您告诉泛型方法参数具有类型约束,这是最常见的。它必须实现一个接口,但使用 c++ 模板对参数类型没有限制。

#include <list>
#include <iostream>
using namespace std;
template <typename T>
void DisplayContents (const T& Input)
{
    for (auto iElement = Input.cbegin() // no intellisense
    ; iElement != Input.cend()
    ; ++ iElement )
    cout << *iElement << ' ';

    cout << endl;
}
int main ()
{
    std::list <int> listIntegers;
    listIntegers.push_front (10);
    listIntegers.push_front (2011);
    listIntegers.push_back (-1);
    listIntegers.push_back (9999);
    DisplayContents(listIntegers);
    //  DisplayContents(99); // neither of these will compile
    //  DisplayContents(new string("")); //
return 0;
}

因此,在模板化方法 DisplayContents(const T& Input) 中,Input 没有智能感知。当您键入句点字符时,不会弹出任何建议(这并不奇怪,因为函数参数没有指定输入必须是列表或任何其他类型的 STL 容器)。

但是,如果您尝试将不是 STL 容器的内容发送到 DisplayContents(const T& Input),那么编译器会抛出以下错误:-

  • 错误 C2100:非法间接
  • 错误 C2228:'.cbegin' 左侧必须有类/结构/联合
  • error C3536: 'iElement': 在初始化之前不能使用

暗示编译器确实知道需要具有一些基本特征的参数类型。

任何人都可以解释一下编译器如何“知道” cbegin() 和 * 运算符可以在作为参数发送列表时使用,而不是在发送字符串或 int 时,显然类型不是被称为智能感知没有采用 cbegin() 方法?

【问题讨论】:

  • 代码实例化后才知道。它尝试使用您提供的任何模板参数来实例化函数体,然后如果函数实际上不是该类型的有效成员,则会给您一个错误。在相关说明中,您可能需要查看 Concepts,这是 C++ 即将推出的功能,它允许您对模板类型提出要求。
  • 补充一点:Intellisense 不能这样做,因为它不检查哪些类型被传递给函数。首先,这很困难,因为它必须查看项目中函数的每个实例化并找到它们成员的交集(并且假设所有实例化都提供正确的类型)。其次,在许多情况下这是不可能的,例如在库头中,您根本无法分辨可能传递的每一种类型。但是,它可以在编译时完成,因为要编译代码,我们必须实例化它。

标签: c++ templates intellisense


【解决方案1】:

其实很简单。编译器会假装T 是您传入的参数类型,然后继续编译。如果它遇到任何错误,那么它将报告这些错误。只要您使用的参数类型工作,如果你硬编码该类型,那么它工作。

在您的情况下,它会因 int 而失败,因为 int 没有 cbegin() 方法。

因为参数类型变成了std::string * const &amp;,所以它失败了new std::string(""),你不能在这个指针上调用.cbegin()。 (您必须改为致电 -&gt;cbegin()。)

但是,您可以使用std::string("") 调用它(注意缺少new),这将导致参数为const std::string &amp;,这将编译。

所以它与编译器“知道T 代表一个标准容器”完全没有关系。如果您使用 cbegin()cend() 方法创建一个简单类型,并确保这些方法的返回值可以递增、取消引用和比较是否相等,那么该类型也可以正常工作。

(您的模板函数的Here is a demo 使用用户定义的类型。)

【讨论】:

  • 我们使用 SFINAE 使编译器更早地失败,因此我们可以为提供不同接口的模板类型提供不同版本的算法。
  • 谢谢@cdhowie,我一直想知道为什么字符串不起作用。尽管如此,如果编译器足够聪明,可以假装该类型确实支持某个方法,为什么智能感知不能在 for 循环声明中提取方法?
  • @Dave00Galloway 因为那时 IDE 不知道 T 代表什么;实际上它可以代表任何类型。很简单,智能感知的任何结果都不会有用。
  • @Dave00Galloway:我们可以有一百层深的模板,每个可能有十几个变体(专业化),具体取决于您的模板参数提供的接口。这是对智能感知的 DOS 攻击...
  • @Dave00Galloway 我添加了一个使用您的模板函数的用户定义类型的示例。
【解决方案2】:

用于制作通用代码的模板。

这里编译器会为你生成三个重载函数。

void DisplayContents (const std::list<int>& Input)
void DisplayContents (const int& Input)
void DisplayContents (string* const & Input)

显然版本 2 和 3 不会编译,键入 const int&amp;string* const&amp; 没有方法 cbegin() 和 cend()

PS:版本 3 参数应该是 string* const&amp; Input,感谢 cdhowie

【讨论】:

  • 正如有人向我指出的那样,第三个签名实际上是std::string * const &amp;(引用指向字符串的常量指针,而不是引用指向常量字符串的指针)。
猜你喜欢
  • 2018-08-11
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多