Вызов виртуальной функции-члена из базового класса (шаблона) - PullRequest
4 голосов
/ 11 ноября 2011

Предположим, у меня есть следующее:

#include <iostream>
#include <string>

template<class T>
class base
{
public:
    void print()
    {
        T t = get();
        std::cout << t << std::endl;
    }

    virtual T get() const
    {
        // assumes T can be constructed from, say, -1
        T t = -1.0;
        return t;
    }
};

class derived : public base<std::string>
{
public:
    virtual std::string get() const
    {
        // this is a silly example, but one can
        // imagine that what we return here could
        // depend on data members of derived
        return "this is a string";
    }
};

int main()
{
    derived d;
    d.print();

    return 0;
}

Мне кажется, что d.print() должен вызвать derived::get(), потому что get() является виртуальным. Тем не менее, я получаю сообщение об ошибке компилятора о том, что не могу инициализировать string до -1.0, что означает, что компилятор пытается вызвать base::get(), когда я вызываю d.print(). Что происходит?

Ответы [ 4 ]

6 голосов
/ 11 ноября 2011

Однако я получаю сообщение об ошибке компилятора, говорящее о том, что я не могу инициализировать строку с -1.0, что означает, что компилятор пытается вызвать base :: get (), когда я вызываю d.print ().

Нет, эта ошибка компилятора означает, что компилятор пытается создать экземпляр base<std::string>::get(), что он должен сделать, потому что derived использует base<std::string> в качестве базового класса. То, что вы не вызываете функцию, не означает, что вы не можете . Вы все еще можете позвонить base<std::string>::get() напрямую.

Вы создали экземпляр base<std::string> и использовали его в качестве базового класса. Поскольку base<std::string>::get() является виртуальной функцией, она считается «используемой» тем, что вы используете base<std::string> в качестве базового класса. Поскольку он используется, он должен быть создан. Таким образом, компилятор должен и попытается скомпилировать функцию.

И поскольку std::string не может быть неявно создан из числа с плавающей запятой, компилятор выдает ошибку из-за неудачной замены шаблона.

4 голосов
/ 11 ноября 2011

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

§3.2p2 [...] Виртуальная функция-член используется odr, если она не является чистой. [...]

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

1 голос
/ 11 ноября 2011

Проблема в этом методе (где T = std :: string):

virtual T get() const
{
    // assumes T can be constructed from, say, -1
    T t = -1.0;
    return t;
}

Компилятор прав.Вы не можете инициализировать std :: string, используя двойное значение.

1 голос
/ 11 ноября 2011
T t = -1.0;

Конечно, это не скомпилируется, если T равно std::string. Это не имеет ничего общего с тем, что get является virtual и какая функция будет вызываться во время выполнения. Время выполнения наступает после того, как код скомпилирован в машинный код, а ваш код даже не скомпилируется.

Почему бы вам не сделать это:

T t = T(); 

Снова требуется T, чтобы иметь конструктор по умолчанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...