14

そのコンテキストでは理解できないというコンパイラ警告が表示されます。次のコードから「Child.cpp」をコンパイルすると。(不思議ではありません: 私はクラス宣言を最小限に取り除いたので、内容はあまり意味がありませんが、問題はすぐにわかります)。Visual Studio 2003およびVisual Studio 2008で最高の警告レベルで警告が表示されます。


コード

AbstractClass.h:

#include <iostream>

template<typename T>
class AbstractClass
{
    public:
        virtual void Cancel(); // { std::cout << "Abstract Cancel" << std::endl; };
        virtual void Process() = 0;
};

// Outside definition. If I comment out this and take the inline
// definition like above (currently commented out), I don't get
// a compiler warning.
template<typename T>
void AbstractClass<T>::Cancel()
{
    std::cout << "Abstract Cancel" << std::endl;
}

Child.h:

#include "AbstractClass.h"

class Child : public AbstractClass<int>
{
    public:
        virtual void Process();
};

Child.cpp:

#include "Child.h"
#include <iostream>

void Child::Process()
{
    std::cout << "Process" << std::endl;
}

警告

クラス「Child」は「AbstractClass」から派生しています。「AbstractClass」には、パブリック メソッド「AbstractClass::Cancel()」があります。クラス本体の外側でメソッドを定義すると(コードのように)、コンパイラの警告が表示されます...

AbstractClass.h(7): 警告 C4505: 'AbstractClass::Cancel': 参照されていないローカル関数が [T=int] で削除されました

...「Child.cpp」をコンパイルすると。これはパブリック関数であり、コンパイラは後でこのメソッドを参照するかどうかを判断できないため、これを理解できません。最後に、このメソッドを参照します。main.cpp で呼び出すためです。このコンパイラ警告にもかかわらず、すべてのファイルをコンパイルしてリンクし、プログラムを実行すると、このメソッドは機能します。

//main.cpp
#include <iostream>
#include "Child.h"

int main()
{
    Child child;
    child.Cancel();  // Works, despite the warning
}

Cancel() 関数をインラインとして定義すると (AbstractClass.h でコメントアウトされたコードとして表示されます)、コンパイラの警告は表示されません。もちろん、私のプログラムは動作しますが、この警告を理解したいのですか、それとも単なるコンパイラの間違いですか?

さらに、AbsctractClass をテンプレート クラスとして実装しないと (この場合はテスト目的のためだけに)、コンパイラの警告も表示されません...?


非仮想関数を作成すると、その非仮想関数のコンパイル警告は表示されませんが、これまでのすべての回答は仮想のものを構成していません。これを試して:

template<typename T>
class AbstractClass
{
    public:
        virtual void Cancel(); // { std::cout << "Abstract Cancel" << std::endl; };
        virtual void Process() = 0;
        void NonVirtualFunction();
};

//...

template<typename T>
void AbstractClass<T>::NonVirtualFunction()
{
    std::cout << "NonVirtualFunction" << std::endl;
}

知っている限りの答えは役に立ちましたが、質問が完全に答えられているとは思いません。

4

7 に答える 7

6

テンプレート化されたクラスに純粋な仮想メソッドがある場合、Visual Studioはこの警告を誤って報告します。この場合、gcc や clang などの他のコンパイラは、この警告を報告しないようです。

テンプレート化されたクラスまたはテンプレート化されていないクラスの純粋仮想メソッドは完全に合理的であり、多くの場合良い考えです。メソッドを純粋仮想として宣言すると、派生クラスで実装する必要があります。

バグとしてこれへの言及はどこにも見つかりませんでした-私はマイクロソフトの開発者プログラムに参加していません。おそらく誰かがこのバグを報告できますか?

于 2012-01-09T19:30:01.233 に答える
3

その警告は意図的なものではないと思います。コンパイラは、関数が翻訳単位に対してローカルであると誤って信じていますが、関数はまったくそうではありません。生成された関数を の他の翻訳単位で使用するとmain、関数使用されます。警告を消すために考え出したさまざまな方法は、コンパイラのバグのあるパスを回避するさまざまな方法のようです。

との違いvirtualは、virtual関数を使用しなくても関数をインスタンス化できることです。これは、通常、クラスが暗黙的にインスタンス化されたときに発生します。標準は有効であると宣言しています(私による強調)

実装は、関数テンプレート、メンバー テンプレート、非仮想メンバー関数、メンバー クラス、またはインスタンス化を必要としないクラス テンプレートの静的データ メンバーを暗黙的にインスタンス化してはなりません。仮想メンバー関数がインスタンス化されない場合、実装がクラス テンプレートの仮想メンバー関数を暗黙的にインスタンス化するかどうかは指定されていません。

この場合、同じ仮想関数の暗黙的なインスタンス化が 2 つあります。の 1 つはChild.h使用せずに行われたため、コンパイラは関数が役に立たないと考えています。しかし、同じ関数が他の場所 ( main.cpp) で使用されているため、その警告は明らかに矛盾しています。

于 2010-06-25T18:18:57.547 に答える
1

警告は、リンカーが関数の使用法を認識できないことを示しています。

リンカに警告を回避するよう「伝え」たい場合は、リンカをだまして「使用中である」ようにさせることができます。

例えば:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

function の警告 C4505 を回避するには十分ですMyLinkerThinkNotUsedFunction

于 2016-01-16T15:38:37.897 に答える
0

Visual Studio 2010 では、派生クラス (子クラス) のコンストラクターを定義すると、この警告は表示されなくなります。

于 2015-06-05T07:53:32.167 に答える
0

通常のテンプレート化されていないクラス メソッドのコードは、コンパイラがメソッドのコードを検出したときにコンパイルされます。

テンプレート化されたクラスの場合、これは異なります。コードはヘッダーにあるため、コンパイラがコードに遭遇するたびにコンパイルする場合、コードによって呼び出されなくても、このメソッドが何度もコンパイルされることを意味します。child.h が 1000 個の他のファイルに含まれているとします。コンパイラに Cancel メソッドを 1000 回コンパイルさせますか、それとも Cancel が実際に呼び出されたときだけコンパイルさせますか?

child.cpp には child.h が含まれていますが、Cancel メソッドは呼び出されません。したがって、Cancel はコンパイルされません (ただし、これに対して警告が表示されるのは奇妙だと思います)。

main.cpp には child.h も含まれており、今回は Cancel メソッドを呼び出します。これは、コンパイラがメソッドをコンパイルするためのシグナルです。最後に、リンカは、コンパイルされた Cancel メソッドのインスタンス化をすべて見つけてマージします。

于 2010-06-16T09:25:28.317 に答える
0

テンプレートは、コード生成の前にインスタンス化されます。これは、テンプレートのコードを生成できるようにするには、コンパイラがテンプレートで使用される特定のクラスを知る必要があることを意味します。そのため、別のユニットでテンプレート クラス メソッドを定義すると、その定義はテンプレートのインスタンス化時に不明になります。

この警告は、定義AbstractClass<T>::Cancelに使用したユニットで のコードが生成されていないことを意味している可能性がありAbstractClass<T>::Cancelます。テンプレートクラスのメソッドは、一度遭遇すると生成される通常のメソッドコードとは対照的に、それらが使用される(つまり、参照される、呼び出される)ときにのみ生成されます。

AbstractClass<T>::Cancelが定義されている ie の関数AbstractClass.cppから呼び出そうとするとCancel、警告は消えます。

于 2010-06-16T09:33:03.723 に答える
-2

関数テンプレートを仮想にすることは違法です。スタック オーバーフローの質問を参照してください Is Making a function template specialization virtual legal? .

于 2010-06-16T09:29:35.773 に答える