Предупреждение компилятора в базовом классе шаблонов С++

Я получаю предупреждение о компиляторе, которое я не понимаю в этом контексте. Когда я компилирую "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;
}

Предупреждение

Класс "Ребенок" происходит от "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() как inline (вы видите это как внекомандовый код в 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;
}

Ответы на вопросы помогли мне, но я не думаю, что на этот вопрос будет дан ответ.

Ответ 1

Я не вижу здесь правильного ответа: если у вас есть чистый виртуальный метод в шаблоне, Visual Studio неправильно сообщает об этом предупреждении. Другие компиляторы, такие как gcc и clang, не сообщают об этом предупреждении в этом случае.

Чистые виртуальные методы в шаблонных или не templated-классах вполне разумны и часто являются хорошей идеей - объявлением метода как чистого виртуального, чтобы вы реализовали его в производных классах.

Я не нашел ссылку на это где-нибудь в качестве ошибки - я не в программе разработчика Microsoft, возможно, кто-то, кто может записать эту ошибку?

Ответ 2

Я не думаю, что это предупреждение намеренно. Компилятор ошибочно полагает, что функция локальна для единицы перевода, но функция не такая вообще. Вы используете сгенерированную функцию в другой единицы перевода от main, поэтому используется функция. Различные способы, по которым вы решили отключить предупреждение, просто кажутся разными способами работы с ошибкой в ​​компиляторе.

Разница в отношении virtual заключается в том, что функции virtual могут быть созданы даже без их использования. Это происходит, когда их класс неявно создается как обычно. Стандарт объявляет, что действительный (выделение мной)

Реализация не должна имплицитно создавать шаблон функции, шаблон-член, не виртуальный член-член, класс-член или статический член данных шаблона класса, который не требует создания экземпляра. Неясно, реализует ли реализация неявно экземпляр виртуальной функции-члена шаблона класса, если бы функция виртуального члена не создавалась иначе.

В этом случае есть две неявные экземпляры той же виртуальной функции. Один в Child.h был выполнен без какого-либо использования, и, таким образом, компилятор считает, что функция бесполезна. Но поскольку эта же функция используется в другом месте (в main.cpp), это предупреждение явно противоречит.

Ответ 3

Код в обычных, не templated методах класса компилируется, когда компилятор встречает код для метода.

Для шаблонных классов это другое. Код находится в заголовке, поэтому, если компилятор будет скомпилировать его каждый раз, когда он встретит код, это будет означать, что этот метод скомпилирован снова и снова, даже если он не вызывается вашим кодом. Предположим, что child.h включен в 1000 других файлов. Вы хотите, чтобы компилятор компилировал метод "Отмена" 1000 раз или только когда на самом деле вызывается "Отмена"?

child.cpp включает child.h, но не вызывает метод Cancel. Поэтому Cancel не скомпилирован (хотя мне странно, что вы получаете предупреждение для этого).

main.cpp также включает child.h, и на этот раз он вызывает метод Cancel, который является сигналом для компилятора для компиляции метода. Наконец, компоновщик найдет все скомпилированные экземпляры метода Cancel и объединит их.

Ответ 4

Шаблоны создаются до генерации кода. Это означает, что компилятор должен знать определенный класс, используемый в шаблоне, чтобы иметь возможность генерировать код для этого шаблона. Таким образом, когда вы определяете свой метод класса шаблона в отдельном модуле, его определение неизвестно во время создания шаблона.

Предупреждение, скорее всего, означает, что код для AbstractClass<T>::Cancel не генерируется в устройстве, которое вы использовали для определения AbstractClass<T>::Cancel. Методы класса шаблона генерируются только после их использования (т.е. Ссылки, называются), в отличие от обычного кода метода, который генерируется после его возникновения.

Если вы попытаетесь вызвать AbstractClass<T>::Cancel из функции в i.e. AbstractClass.cpp, где Cancel определено, предупреждение должно исчезнуть.

Ответ 5

В Visual Studio 2010 это предупреждение исчезает, если вы определяете конструктор для производного класса (ваш дочерний класс).

Ответ 6

В предупреждении просто говорится, что компоновщик не видит использования функции.

Если вы хотите "рассказать" компоновщику, чтобы избежать предупреждения, вы можете обмануть компоновщика в "думать", что он используется.

Например:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

будет достаточно, чтобы избежать предупреждения C4505 о функции MyLinkerThinkNotUsedFunction.