2014-11-26 2 views
1

Ошибка в методе, основанном на расширении шаблона, дает ошибку компилятора, когда метод явно вызван. Хотя, когда этот метод отмечен как виртуальный, он создает ошибку компилятора независимо от того, действительно ли он вызван. Есть ли что-нибудь в стандарте C++, объясняющее, почему маркирование этих методов как виртуальных приводит к ошибке компилятора?Наследование с возможной ошибкой шаблона

#include <memory> 
#include <iostream> 

template <class T_> 
class Foo 
{ 
protected: 
    T_ data; 
public: 
    Foo(const T_& x) : data(x) { } 

    Foo(T_&& x) : data(std::move(x)) { } 

    // comment these two lines out and it works fine. 
    virtual void test(T_& x) = 0; 
    virtual void test(T_&& x) = 0; 
}; 

template <class T_> 
class Bar : public Foo<T_> 
{ 
public: 
    using Foo<T_>::Foo; 

    void test(T_& x) 
    { 
     std::cout << "test(&)" << std::endl; 
     x = this->data; 
    } 

    void test(T_&& x) 
    { 
     std::cout << "test(&&)" << std::endl; 
     x = std::move(this->data); 
    } 
}; 

int main() 
{ 
    Bar<std::unique_ptr<int>> x(std::unique_ptr<int>(new int(42))); 
} 

ответ

2

В overriders из virtual функций всегда УСО-используется - то есть, их определение должно присутствовать, являются ли они явно используются в ЕП или нет.
Овердрайзеры virtual функции сами по себе virtual ([class.virtual]/2) и все функции virtual должны быть определены ([basic.def.odr]/3 и 4).

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

Если не является членом шаблона класса [...] был явно инстанциированы явно специализированы, специализация элемента неявно инстанцирован, когда специализация ссылки в контекст, который требует определения члена;

Для виртуальных функций можно утверждать, что их присутствия достаточно, чтобы требовать определения. Однако стандарт не связывает себя вниз и оставляет решение реализации, [temp.inst]/11:

Это неопределенная реализация неявно конкретизирует ли не виртуальную функцию-члена класса шаблон, если бы виртуальная функция-член иначе не создавалась.

+0

Также актуальным является [temp.inst]/p11 - «Реализация не должна неявно создавать [...] не виртуальную функцию-член [...] шаблона класса, которая не требует инстанцирования. независимо от того, реализует ли реализация неявно экземпляр виртуальной функции-члена шаблона класса, если бы виртуальная функция-член не создавалась иначе ». –

+0

Декларации * * создаются при создании экземпляра класса. Определения * не являются *, по умолчанию ([temp.inst]/p1). Общее правило заключается в том, что «специализация члена неявно создается, когда специализация ссылается в контексте, требующем определения члена» ([temp.inst]/p2). Используется ли * odr-used *, поскольку такой контекст неясен. Несмотря на это, [temp.inst]/p11 дает возможность компилятора создать экземпляр виртуальной функции-члена, которая иначе не была бы создана. –

+0

@ T.C. Да, я просто нашел ту же цитату. Я не мог понять, когда существует требование, но это, по-видимому, нечеткость самого стандарта. – Columbo

0

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

int main() 
{ 
    Bar<std::unique_ptr<int>> x(std::unique_ptr<int>(new int(42))); 
    x.test(std::unique_ptr<int>(new int(99))); 
    std::unique_ptr<int> pi; 
    x.test(pi); 
    std::cout << "test returned " << *pi << std::endl; 
} 

версия принимает ссылку не будет создаваться в std::unique_ptr не может быть назначена.