2012-11-29 9 views
13

JosuttisとVandevoordeのよく知られたテンプレートの本C++ Templates: The Complete Guideでは、関数テンプレートのオーバーロードに関する詳細について説明しています。私が構築し、同じコードをコンパイルするとき、しかしJosuttisから:特定の型を与えられた同じ関数シグネチャにインスタンス化する異なるテンプレート関数は、ODR無効になりますか?

This program is valid and produces the following output: 

(Note: Output shown below) 

関数シグネチャの議論に関連し、その例の一つでは、

とオーバーロードされた関数テンプレートは、彼らは、次の用語で記述したコードを提示しますVisual Studio 2010では、別の結果が得られます。これにより、VS 2010コンパイラが間違ったコードを生成しているか、Josuttisがコードが有効であると間違っていると考えられます。

ここにコードがあります。 (Josuttis氏2003年、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'); 
} 

(2つのテンプレート関数定義の型引数の逆転に注意してください。注また、この逆転は時に影響を与えないこと彼らは、このコード例では2つの機能f1()ためのものであるように、2つの型引数は、同じである)

Josuttis氏によると:。

This program is valid and produces the following output: 

f1(T2, T1) 
f1(T1, T2) 
私は、コンパイラ/リンカは区別することが可能であるかと思いまして、さらに

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

私は構築およびVisual Studio 2010のコンパイラでは、変わらず同じコードを実行は、ここに私の結果であり、ファンクションf1はfile1.cppでインスタンス化され、ファンクションf1はfile2.cppでインスタンス化されているので、コンパイラはこれらの関数がテンプレートから作成されたという知識のすべてを取り去り、関数シグネチャ自体の情報(私は考える):void (char, char)これはf1の関数で同じです。

機能シグネチャが2つの翻訳単位で同じなので、これはOne Definition Rule(ODR)の違反の例であると考えられますので、C++は無効です。

しかし、私がちょうど指摘したように、JosuttisとVandevoordeは、これが有効で C++であると主張しています。

しかし、同じコードの私のコンパイルされたバージョンは、異なる結果が得られるので、Josuttis氏が出力されていると主張するよりも、これはVS 2010のいずれかが不正なコードを生成している、またはJosuttis氏は、この場合は不正確であることを示す指標(すなわちのようです、コードは無効であり、ODRに違反しています)。

JosuttisとVandevoordeは間違っていますか、VS 2010の出力が正しくありませんか?または、VS 2010の出力と、Josuttisの出力との差異を説明する他の説明がありますか?

f1()が呼び出された時点でVS 2010の逆アセンブリを表示することは重要です。

(直接main()以内)f1()の最初の呼び出し:

f1() called directly from within main()

g()内から)f1()の第二の呼び出し:

f1() called from within g()

f1()のアドレスそのどちらの場合もコンパイラによって選択されるのは同じです - 13E11EAh。実際、コンパイラは2つのインスタンス化された関数のシグネチャを区別できないことを示しています。これはODRが違反しているため、コードはで無効です。C++であり、Josuttisには誤りがあります。しかしそれはちょうどその兆候です。知りません。

(私は本ウェブサイト上の正誤表をチェックして、この例の言及がない。)コメントからのリクエスト毎の

補遺、私はのために、.MAPファイルから関連出力を添付していますマングルされた名前を示し、このプログラムは、/ f1に使用されている(S)

.map file output showing mangled names for <code>f1</code>

今の質問は答えていることを補遺2 - Josuttis氏の本が正しいか - 私はあることに注意したいですJosuttisテキストでは、同じセクション(12.2.1)に、ユニークな関数シグネチャを決定するものを明示的に示しています。には、テンプレートアスペクトが含まれています。

(関数シグネチャを定義する他のものの中でも)TRANSLATION UNITは関数シグニチャの一部です。テンプレート関数の場合は、RETURN TYPEは関数シグネチャの一部であり、

.6です。関数が関数テンプレートから生成されている場合は、テンプレートパラメータとテンプレート引数。

したがって、明らかです。テンプレート情報は、関数テンプレートがインスタンス化された後でも、コンパイラ/リンカーが(私の質問のコード例の場合のように)テンプレートに必要な特別な規則に従うために、コンパイラによって維持され、追跡されなければなりません。

+0

問題の機能に実際にマングルされた名前を追加することは非常に便利です。 –

+0

@Pedroどうすれば入手できますか?私は今それを得る方法を見るために見ていきます... –

+0

これについてこのように考えてみましょう:すべてのテンプレートを1つのファイルに含めた場合、コードは正確ではありません(特殊化があいまいなので)。そのため、コードを別のファイルに分割するだけで、コードが正しくなるべきではありません。 –

答えて

8

これまでの間違った回答のお詫び。この例は確かに正しいと思われますが、実際には標準自体にも同様の例があります(C++ 11,14.5.6.1/1-2)。私はちょうどその全体でそれを引用してみましょう:

  1. 2つの異なる関数テンプレートの特殊化は、同じ型を持っているように、関数テンプレートをオーバーロードすることが可能です。 [例:

    // 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*) 
    } 
    

    から端例]

  2. このような特殊化は、別個の機能であり、一つの定義ルール(3.2)に違反しません。あなたのケースでは

、2つの異なる関数テンプレートを持って、両方が(あなたが関数テンプレートをオーバーロードすることができますので、細かいです)f1と呼ばれる、と彼らは同じ型を持つ特殊化を持って起こります。

+0

@ Xeo:私の予想とは異なり、私は実際には、異なるTU内の特定の署名に対して異なるオーバーロードセットを持つことを禁じているルールを見つけることはできませんでした。そのようなルールはありますか?ポイントは、OPには同じ型(つまり 'void(char、char)')がありますが、単に異なる関数である2つの異なる関数 'file1 :: f1 'と 'file2 :: f1 'があります。 –

+0

私が言ったことを取り戻します。 :)関数テンプレート定義は実際には異なり、同じTUに入れることができます。あなたが得る唯一の問題は 'f1'を呼び出す際にあいまいさがあることです。だから、これはVC++バグのようです。私はもう一度チェックします。 – Xeo

+0

私は助けに感謝します。私の質問でコードが(私の質問で)有効なC + +であれば、それは、コンパイラがコンパイルされたコードに元のテンプレート定義を含む何らかの方法で情報を含んでいなければならないということを暗示するODR違反?しかし、私は、関数のテンプレートの起源はコンパイルされていると思ったのですか? –

関連する問題