【问题标题】:Why uncalled template class members aren't instantiated?为什么未实例化未调用的模板类成员?
【发布时间】:2012-06-28 20:28:17
【问题描述】:

我想知道,当我创建具有指定模板类型参数的类模板实例时。
1) 为什么未调用的函数没有被实例化? .
2) 在我尝试使用它之前,它们不会被编译吗?
3) 这种行为背后的逻辑是什么?

示例

template <class T>
class cat{
public:

T a;
void show(){
   cout << a[0];
}
void hello(){
   cout << "hello() get called \n";
}
}; 

int main(){
cat<int> ob1; // I know that show() did not get instatiated, otherwise I will get an    error since a is an int
ob1.hello();

 }

【问题讨论】:

    标签: c++ class templates


    【解决方案1】:

    模板不是代码 - 它们是用于制作实际代码的模式。在您提供参数之前,模板并不完整,因此无法提前制作代码。如果您不使用一组特定的模板参数调用函数,则永远不会生成代码。

    【讨论】:

    • 不是 100% 正确,仅适用于隐式实例化。在显式模板实例化中,所有函数都生成(这意味着设计决定不总是这样做,并且只给出了一半正确的答案:它重复了问题陈述而没有提供太多解释)
    【解决方案2】:

    如果他们实例化了整个类,那么你可能会得到无效的代码。

    你并不总是想要那个。

    为什么?因为在 C++ 中,“仅当 X、Y 和 Z 为真时才编译此代码”是困难(据我所知,在某些情况下,完全不可能)。

    例如,你会怎么说“如果嵌入的对象可以被复制,只有我的复制构造函数”?据我所知,你不能。

    所以他们只是让它们无法编译,除非你真正调用它们。

    【讨论】:

    • @AlexDan:只有模板。大概,对于常规类,您已经了解它们的一切,所以没有什么可以阻止的——只是不要包含无法编译的代码。但是,对于模板,您不知道将使用什么实例化它们,但您仍然希望能够编写普通代码——因此编译器会帮助您这样做。
    • @AlexDan:是的,没错。但是,请注意,您不能真正测试这本身 - 只要您提到名称“hello”,编译器就会查找该名称并尝试实例化模板(如果存在) .如果不实际提及您想要的成员的名称,就无法告诉 C++“给我所有成员”,所以这对您来说有点透明。
    • @AlexDan:不用担心。 :-) 好吧,这取决于您所说的“创建”是什么意思。它的 space 分配在你实例化它的地方——成员变量是已知的。 函数在被调用之前并不完全为人所知,但它们的存在并不会真正以任何方式影响任何特定的实例
    • @AlexDan: 直到程序结束才会完全创建对象 -- 并非如此,类型本身将在您创建第一个实例时创建,每个实例的成员函数将在 (odr-) 使用时实例化。 何时函数被实际编译是一个不同的问题,其中的细节并不真正相关(模板函数的翻译发生在链接之前的最后一步,在所有非模板函数都被翻译之后,但是实例化点是函数的第一个odr-use之前之前的适当位置)
    • @AlexDan:它们类的成员,无论它们是否被实例化,但编译器不会生成代码。我的意思是,除非您使用该函数(C++11 中的odr-use),否则不会为该函数生成任何代码。将这些函数视为已声明但未定义。另请注意,成员函数不是该类型的每个实例的一部分,而是共享的。
    【解决方案3】:

    稍微修饰一下:这通常称为duck typing,底线是它允许您编写“类模式”,其中一些成员函数在用一个实例化时可能会应用模板类型,other 成员可以在使用第二种模板类型实例化时应用,并且只需要您实际调用的那些进行编译,您可以为最终成为常见操作的操作编写更少的代码.

    通过不要求编译所有成员函数,您可以获得对实际编译的函数进行静态类型检查的所有细节。

    例如,假设您有:

      template <typename E> 
      class myContainer {
    
          // Imagine that constructors, setup functions, etc. were here
    
          void sort();   // this function might make sense only if E has an operator< defined
    
          E max();   // compute the max element, again only makes sense with a operator<
    
          E getElement(int i);  // return the ith element
    
          E transmogrify();  // perhaps this operation only makes sense on vectors
      };
    

    那么你有

      // sort() and getElement() makes total sense on this, but not transmogrify()
      myContainer<int> mci;         
    
      // sort and max might not be needed, but getElement() and transmogrify() might
      myContainer<vector<double>> mcvd;        
    

    【讨论】:

    • 我对每个答案都投了反对票,对这个答案投了赞成票。最后一个不声称非模板成员函数是模板
    • @JohannesSchaub-litb:如果非模板成员函数不是模板。那为什么在我使用它之前它不会被激活?
    • @AlexDan Johannes 的区别在于 class 是模板而不是成员函数,但只有被调用的函数才会被实际编译。
    • @AlexDan Johannes 有点像语言律师,但他是对的。在 C++03 标准第 14.5.1.1 节第 2 段中,它说:“类模板的成员函数的模板参数由调用成员函数的对象类型的模板参数确定。”因此,他声明成员函数不是模板。第 14.5.2 节进一步解释了他为何做出这种区分,即,您可以拥有与类模板分开模板化的成员函数。
    【解决方案4】:

    没有为cat&lt;int&gt;::show() 生成代码,因为您从不调用它。如果你调用它,你会得到一个编译错误。从未调用过的模板函数不存在。

    模板只不过是测试替换机制。这使他们非常强大。作为程序员,您可能想要创建一个cat&lt;int&gt;,因为您知道您永远不会调用show() 或调用任何其他无效的东西。编译器会让你知道你是否这样做了,所以效果很好。

    因此,如果您的问题是“为什么它会这样工作”,我会问您“为什么不”?这是一个设计选择。这种选择使我可以安全地使用模板类型,并且仍然可以从代码的其他部分中受益。有什么害处?您还生成了更少的代码,这是一件好事,对吧?

    【讨论】:

    • 我很确定 OP 已经知道这一点。这个问题清楚地说明了“为什么”。
    • @Mehrdad:在看到您的评论之前,我刚刚添加了一点。我想我不明白; “为什么”?为什么不?它很安全,让我作为程序员可以做更多事情,那么为什么人为地限制模板的使用呢?
    • @Mehrdad:任何“为什么”的答案语言设计中的问题是“因为设计师如此决定”。在设计中必须做出艰难的选择,而且往往没有明确明确的理由说明他们为何选择这样或那样的方式。
    • @abelenky:语言设计师不会为了做决定而掷硬币(据我所知:P)。他们的决定背后往往有一个很好的理由。特别是对于像 C++ 这样的语言。
    • @EdS。在正常的普通类中,当我实例化此类的对象时,我现在已经创建了所有成员。所以我不明白的是,这是否也适用于类模板,并且在我尝试调用它之前,成员函数是否显示为 ob1 对象的成员。提前致谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多