从Josuttis:做不同的模板函数,实例化给相同的函数签名给定特定类型,导致ODR无效? [英] From Josuttis: Do different template functions, that instantiate to the same function signature given particular types, result in ODR invalidity?

查看:133
本文介绍了从Josuttis:做不同的模板函数,实例化给相同的函数签名给定特定类型,导致ODR无效?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Josuttis和Vandevoorde的着名的模板书中, C ++模板:完整指南,他们讨论细节关于函数模板的重载。



在他们的一个例子中,与功能签名和重载函数模板的讨论有关,他们提供代码,他们在下面的术语:

 此程序有效,并产生以下输出:

(注意:但是,当我在Visual Studio 2010中构建和编译相同的代码时,我得到了不同的结果。这导致我相信VS 2010编译器生成不正确的代码,或者Josuttis不正确的代码是有效的。



这是代码。 (Josuttis 2003,Section 12.2.1)

  // File1.cpp 

#include< iostream>

template< typename T1,typename T2>
void f1(T2,T1)
{
std :: cout< f1(T2,T1)< std :: endl;
}

extern void g();

int main()
{
f1< char,char>('a','b');
g();
}

...

  // File2.cpp 

#include< iostream>

template< typename T1,typename T2>
void f1(T1,T2)
{
std :: cout< f1(T1,T2)<< std :: endl;
}

void g()
{
f1< char,char>('a','b');
}

(注意两个模板函数定义中类型参数的反转。当这两个类型参数是相同的,因为它们对于这个代码示例中的两个函数 f1(),这种反转没有效果。)



根据Josuttis:

 此程序有效并产生以下输出:

f1(T2,T1)
f1(T1,T2)

当我构建和运行相同的代码,不变,在Visual Studio 2010编译器,这里是我的结果:

  f1 ,T2)
f1(T1,T2)

编译器/链接器可以区分在file1.cpp中实例化的函数 f1 和函数 f1 实例化在file2.cpp中,给定(我认为)编译器剥离所有的知识的事实,这些函数是从模板创建的,并且只有函数签名本身的信息(我想): void(char,char),这两个 f1 函数相同。



由于(如果我是正确的)函数签名在两个翻译单元是相同的,我认为这是违反一个定义规则(ODR),因此它将是无效的C ++。



只是注意到,Josuttis和Vandevoorde声称这是 C ++。



但是因为我的编译版本的相同的代码给了不同结果比Josutt的声明是输出,这似乎是一个迹象表明VS 2010生成不正确的代码,或Josuttis在这种情况下不正确(即,代码是无效的,违反ODR)。



Josuttis和Vandevoorde不正确,或者VS 2010产生不正确的输出?还是有一些其他解释解释输出VS 2010产生的差异和输出Josuttis报告?



可能有兴趣显示VS 2010反汇编调用每个 f1()的点。



第一次调用 f1 )(直接在 main()内):



>



第二次调用 f1()(从 g()内):





注意,编译器在这两种情况下选择的 f1()的地址是相同的 - 13E11EAh。对我来说,这表明事实上,编译器不能区分两个实例化的函数签名,这是一个违反ODR的情况,因此代码是无效的C ++ 和Josuttis有一个错误在他的书。但它只是 - 一个迹象。我不知道。



(我已经检查了书的网站上的勘误,没有提到这个例子。)



ADDENDUM 根据评论的请求,我为此程序附加.map文件的相关输出,显示用于 f1



的修改名称

ADDENDUM 2 '书是正确的 - 我想注意在Josuttis的文本中,在同一节(12.2.1),它明确地列出了什么确定一个唯一的函数签名,包括模板方面。 / p>

从文本(定义函数签名的其他预期事件)中,翻译单元是函数签名的一部分;对于模板函数(仅),RETURN TYPE是函数签名的一部分,


。模板参数和模板参数(如果函数是从函数模板生成的)。


因此 - 很清楚。模板信息必须由编译器维护和跟踪,即使在函数模板被实例化之后,为了使编译器/链接器遵守模板的必要特殊规则(如我的问题中的代码示例)。 / p>

解决方案

对早期,不正确的答案表示歉意。该示例确实似乎是正确的,实际上在标准本身(C ++ 11,14.5.6.1/1-2)中有一个类似的示例。让我简单地引用它:



  1. 可以重载函数模板,两个不同的功能模板特化具有相同的类型。 [示例:

      // file1.c 

    template< class T> void f(T *);

    void g(int * p){
    f(p); // call f< int>(int *)
    }


    // file2.c

    template< class T> void f(T);

    void h(int * p){
    f(p); // calls f< int *>(int *)
    }

    - end example ]


  2. 这些特殊化是不同的功能,不违反一个定义规则(3.2)。


在你的情况下,你有两个不同的函数模板,都叫 f1 很好,因为你可以重载函数模板),并且他们碰巧有具有相同类型的专业化。


In Josuttis' and Vandevoorde's well-known book on templates, C++ Templates: The Complete Guide, they discuss details regarding the overloading of function templates.

In one of their examples, related to a discussion of function signatures and overloaded function templates, they present code that they describe in the following terms:

This program is valid and produces the following output:

(Note: Output shown below)

However, when I build and compile the identical code in Visual Studio 2010, I get a different result. This leads me to believe that either the VS 2010 compiler is producing incorrect code, or that Josuttis is incorrect that the code is valid.

Here is the code. (Josuttis 2003, Section 12.2.1)

// File1.cpp

#include <iostream>

template<typename T1, typename T2>
void f1(T2, T1)
{
    std::cout << "f1(T2, T1)" << std::endl;
}

extern void g();

int main()
{
    f1<char, char>('a', 'b');
    g();
}

...

// File2.cpp

#include <iostream>

template<typename T1, typename T2>
void f1(T1, T2)
{
    std::cout << "f1(T1, T2)" << std::endl;
}

void g()
{
    f1<char, char>('a', 'b');
}

(Notice the reversal of type arguments in the two template function definitions. Note also that this reversal has no effect when the two type arguments are the same, as they are for the two functions f1() in this code example.)

According to Josuttis:

This program is valid and produces the following output:

f1(T2, T1)
f1(T1, T2)

When I build and run the identical code, unchanged, in the Visual Studio 2010 compiler, here is my result:

f1(T1, T2)
f1(T1, T2)

Further, I was wondering how it is possible for the compiler/linker to distinguish between the function f1 as instantiated in file1.cpp, and the function f1 as instantiated in file2.cpp, given that (I think) the compiler strips away all "knowledge" of the fact that these functions were created from templates, and has only the information (I think) of the function signature itself: void (char, char), which is the same for both f1 functions.

Since (if I'm correct) the function signature is identical in the two translation units, I would think that this is an example of a violation of the One Definition Rule (ODR), and that it would therefore be invalid C++.

However, as I've just noted, Josuttis and Vandevoorde claim that this is valid C++.

But since my compiled version of the same code gives different results than Josuttis claims is the output, this seems to be an indication that either VS 2010 is producing incorrect code, or Josuttis is incorrect in this case (i.e., the code is invalid and violates the ODR).

Are Josuttis and Vandevoorde incorrect, or is VS 2010 producing incorrect output? Or is there some other explanation that explains the discrepancy between the output VS 2010 produces, and the output Josuttis reports?

It might be of interest to show the VS 2010 disassembly at the point that each f1() is called.

The first call of f1() (directly within main()):

The second call of f1() (from within g()):

Note that the address of f1() chosen by the compiler in both cases is the same - 13E11EAh. To me, this indicates that in fact, the compiler cannot distinguish between the two instantiated function signatures, and this is a case where the ODR is being violated, so the code is invalid C++ and Josuttis has an error in his book. But it's just that - an indication. I don't know.

(I have checked the errata on the book's website, and there is no mention of this example.)

ADDENDUM Per a request from a comment, I am attaching relevant output from the .map file for this program that shows the mangled name/s being used for f1:

ADDENDUM 2 Now that the question is answered - Josuttis' book is correct - I want to note that in the Josuttis text, in the same section (12.2.1), it is explicitly outlined exactly what determines a unique function signature, including the template aspect.

From the text (among the other, expected things that define a function signature), the TRANSLATION UNIT is part of the function signature; for template functions (only) the RETURN TYPE is part of the function signature, and

.6. The template parameters and the template arguments, if the function is generated from a function template.

Therefore - it is clear. Template information must be maintained and tracked by the compiler even after a function template has been instantiated, in order for the compiler/linker to obey the necessary, special rules for templates (as in the case of the code example in my question).

解决方案

Apologies for the earlier, incorrect answer. The example does indeed seem to be correct, and there is actually a similar example in the standard itself (C++11, 14.5.6.1/1-2). Let me just quote it in its entirety:

  1. It is possible to overload function templates so that two different function template specializations have the same type. [ Example:

    // file1.c
    
    template<class T> void f(T*);
    
    void g(int* p) {
        f(p); // calls f<int>(int*)
    }
    
    
    // file2.c
    
    template<class T> void f(T);
    
    void h(int* p) {
        f(p); // calls f<int*>(int*)
    }
    

    end example ]

  2. Such specializations are distinct functions and do not violate the one definition rule (3.2).

In your case, you have two different function templates, both called f1 (which is fine because you can overload function templates), and they happen to have specializations which have the same type.

这篇关于从Josuttis:做不同的模板函数,实例化给相同的函数签名给定特定类型,导致ODR无效?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆