2015-04-21 2 views
3

Мы все знаем, шаблон класса C++ не создает функции-члены, которые не используются, как показано на рисунке ниже:Полный экземпляр шаблона, принудительный в наложении шаблона C++?

template<typename T> 
class A 
{ 
    public: 
    void WrongFunction(T t); 
    void RightFunction(T t); 
}; 

template<typename T> 
void A<T>::WrongFunction(T t) 
{ 
    std::vector<T> v; 
    auto a = "abc" - v; 
} 

template<typename T> 
void A<T>::RightFunction(T t) 
{ 
    std::cout << "Success" << std::endl; 
} 

int main() 
{ 
    A<int> a; 
    a.RightFunction(2); 
    //a.WrongFunction(1); 
    return 0; 
} 

Поскольку WrongFunction не вызывается в основном, нет никакого фактического кода, генерируемого для него, и, следовательно, нет компиляции возникает ошибка.

Теперь давайте введем абстрактный базовый класс, который определяет интерфейс для класса А (в основном, наследования шаблонов):

template<typename T> 
class Base 
{ 
    public: 
    virtual void RightFunction(T t) = 0; 
    virtual void WrongFunction(T t) = 0; 
}; 

template<typename T> 
class A : Base<T> 
{ 
    public: 
    void WrongFunction(T t) override; 
    void RightFunction(T t) override; 
}; 

template<typename T> 
void A<T>::WrongFunction(T t) 
{ 
    std::vector<T> v; 
    auto a = "abc" - v; 
} 

template<typename T> 
void A<T>::RightFunction(T t) 
{ 
    std::cout << "Success" << std::endl; 
} 

int main() 
{ 
    A<int> a; 
    a.RightFunction(2); 
    //a.WrongFunction(1); 
    return 0; 
} 

Неожиданно компилятор отказывается работать:

prog.cc: In instantiation of 'void A::WrongFunction(T) [with T = int]': prog.cc:39:1: required from here prog.cc:24:20: error: no match for 'operator-' (operand types are 'const char [4]' and 'std::vector >') auto a = "abc" - v;

Мое понимание рабочего потока, в основном, я говорю, создавая экземпляр A. Fine, тогда компилятор находит объявление шаблона для A (обратите внимание, что A не является классом, A<SomeType> is.). Ничего себе, это зависит от Base<int>. Хорошо, компилятор затем находит объявление шаблона для Base, подключает int к позиции, удерживаемой T, - теперь у нас есть объявление для класса Base<int>, но определение не создается - ведь мы не предоставили шаблон для генерации определения для Base<SomeType>, и никто никогда не создавал ни одного экземпляра Base<int> или вызывал функцию в экземпляре. Хорошо. Затем компилятор расширяет объявление Base<int> и генерирует объявление A<int>. Подождите, на следующей строке вызывается RightFunction. Таким образом, компилятор находит определение шаблона для RightFunction для A и подключает определенный тип int и генерирует определение функции-члена для A.

Поскольку WrongFunction никогда не вызывается (никакая специализация не задействована ни в одном явном экземпляре), компилятор не должен даже пытаться генерировать код для A<int> :: WrongFunction --- мой вопрос в том, что происходит, черт возьми?

Компилятор: GCC 4.9.2

Спасибо.

+4

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

ответ

4

С N3337, §14.7.1/10 [temp.inst]

An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated. ...

Так что это законно реализация для конкретизации функции виртуального члена, даже если вы никогда не называли.

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

+0

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

+0

@Yakk Эй, Якк приятно тебя снова увидеть :) Но я не думаю, что это возможно. Виртуальный просмотр таблицы - это поведение времени выполнения. Компилятор вообще не имеет понятия, какая функция действительно вызывается из кода 'basePtr-> SomeFunction();'. Я думаю, что, вероятно, поэтому (просто слишком сложно понять это) стандарт C++ формально запрещает неявное создание экземпляра, даже если поведение отличается от случая без наследования. – h9uest

+0

@ h9uest Если никто не получает доступ к записи vtable во всей программе и не экспортируется, то под as-if ее можно устранить. И по стандарту, это не MANDATED, что виртуальная функция существует только потому, что она виртуальна. Для этого требуется, чтобы компилятор проанализировал всю программу для доступа к vtable, прежде чем она создаст функцию, о которой не знает компилятор, но должен быть совершенно законным. – Yakk

Смежные вопросы