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

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

Мне помогли известные ответы, но я не думаю, что на этот вопрос есть полный ответ.


person eike    schedule 16.06.2010    source источник
comment
MSDN как бы объясняет то, что также объясняется в нескольких сообщениях ниже: msdn.microsoft. ru / en-us / library / z85eyax0.aspx   -  person jilles de wit    schedule 16.06.2010
comment
Но предупреждающее объяснение в этом случае мне не помогло. Смысл предупреждения был бы мне понятен в любом другом случае, но здесь моя проблема была вызвана материалом шаблона. В любом случае спасибо за ваши усилия.   -  person eike    schedule 16.06.2010
comment
Что произойдет, если вы отметите Cancel() как inline, оставив его вне определения класса?   -  person sbk    schedule 16.06.2010
comment
@sbk: если я помечу его как встроенный, но оставлю определение кода снаружи, я не получу предупреждения.   -  person eike    schedule 16.06.2010
comment
Даже если вы не используете шаблон, но не используете встроенные методы, я думаю, вы можете получить некоторые ошибки, если используете AbstractClass.h в других файлах, помимо Child.h. Например, есть еще один файл AnotherChild.h, включающий AbstractClass.h, который также включен в main.cpp.   -  person leiiv    schedule 18.06.2010


Ответы (7)


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

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

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

person Tom Swirly    schedule 09.01.2012
comment
Предупреждение можно обойти, указав виртуальную функцию встроенной. - person mcmcc; 19.02.2013

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

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

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

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

person Johannes Schaub - litb    schedule 25.06.2010

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

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

Например:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

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

person user3409863    schedule 16.01.2016

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

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

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

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

person Patrick    schedule 16.06.2010
comment
Спасибо за ваше объяснение. Это действительно странно, что я получаю предупреждение в этом случае, и, конечно же, текст предупреждения на самом деле вводит в заблуждение. Однако я только что отредактировал свою исходную запись и добавил поведение невиртуальной функции, при которой я не получаю предупреждения. При таком поведении предупреждение компилятора еще больше похоже на странное поведение компилятора. Но, может быть, у вас тоже есть хорошее объяснение ...? Спасибо за ваши усилия! - person eike; 16.06.2010
comment
Хорошо, теперь я понимаю, почему предупреждение. Если метод не виртуальный, то вы уверены, что он не нужен, если он не вызывается. Если он виртуальный, он все равно может вызываться пользователем родительского класса, и его оптимизация может привести к неполной vtable. Однако я не знаю, есть ли способ «заставить» генерацию кода (вместо предупреждения). - person Patrick; 16.06.2010
comment
Вы НЕ ХОТИТЕ генерацию кода. Вы НЕ МОЖЕТЕ оптимизировать чисто виртуальный метод! Предупреждение просто неверное. - person Tom Swirly; 09.01.2012

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

person Lukáš Bednařík    schedule 05.06.2015

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

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

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

person Andrei Sosnin    schedule 16.06.2010

Создание виртуального шаблона функции незаконно. См. Вопрос о переполнении стека Законно ли делать специализацию шаблона функции виртуальной?.

person Pardeep    schedule 16.06.2010
comment
Но сами виртуальные функции не являются шаблонами функций. У меня есть шаблон класса, который содержит «обычные» виртуальные функции. - person eike; 16.06.2010
comment
У вас не может быть шаблона виртуальной функции - сколько слотов вы бы поместили в vtable? Но это не шаблон функции! Эта конструкция работает отлично - она ​​только выдает предупреждение, и то только на VS. - person Tom Swirly; 09.01.2012