Можно ли заменить виртуальные функции автоматическими параметрами? - PullRequest
0 голосов
/ 30 мая 2018

Этот вопрос следует за stackoverflow.com / q / 2391679

Одним из классических примеров функций virtual является

class Shape
{
public:
    virtual string draw() = 0;
};

class Circle : public Shape
{
public:
    string draw() { return "Round"; }
};

class Rectangle : public Shape
{
public:
    string draw() { return "Flat"; }
};

void print (Shape& obj)
{
    cout << obj.draw();
}

Однако мывместо этого можно передать параметр auto в C ++ 14

class Circle
{
public:
    string draw() { return "Round"; }
};

class Rectangle
{
public:
    string draw() { return "Flat"; }
};

void print (auto& shape)
{
    cout << shape.draw();
}

Когда нам следует отдавать предпочтение virtual функциям или auto параметрам?

Является ли последний более эффективным из-за раннего связывания?

1 Ответ

0 голосов
/ 30 мая 2018

C ++ имеет два различных механизма для написания фрагмента кода, который ведет себя по-разному в зависимости от типов объектов, на которые действуют:

  • virtual функций и наследования, которые работают на * 1005Шаблонные функции * runtime и
  • , работающие в время компиляции .

Ваш пример с параметрами auto (которые, по-видимому, на самом деле не были приняты в C ++ 14 за исключением лямбда-функций ) работает с шаблонами.Код, который вы написали, эквивалентен

template <typename T>
void print(T& shape) {
    cout << shape.name();
}

Этот код предполагает, что тип T может быть определен во время компиляции , так как компилятор должен знать типT для заполнения шаблона.Как только компилятор узнает об этом, он может сказать: «Ах, я знаю, что это за тип! Я сгенерирую код для непосредственного вызова функции name этого типа, и я точно знаю, какую функцию он будет вызывать».

С другой стороны, виртуальные функции и наследование работают при времени выполнения .Например, предположим, что вы хотите написать функцию, которая считывает некоторые данные из сети, а затем возвращает Circle или Rectangle.У вас может быть такой код:

Shape* myShape = decodeNetworkData();

Здесь все, что знает компилятор, это то, что myShape указывает на что-то вроде Shape, но он не может определить, является ли это круг или квадрат,Поэтому, если бы вы позвонили

cout << myShape->name();

, то компилятор сказал бы: «Я знаю, что вы вызываете какую-то версию name, но я не знаю, какую. Но это нормально! Я»Я сгенерирую некоторый код, который смотрит на динамический тип myShape (тип того, на что он фактически указывает) и использует его для поиска, какую функцию вызывать. "

Обратите внимание, что код компилятор будетГенерирование в каждом случае отличается и поведение будет отличаться.В первом случае компилятор точно знает, какую функцию вызывать.Во-вторых, компилятор не знает, какую функцию вызывать, и должен сгенерировать дополнительный код, чтобы все заработало.Но, с другой стороны, если у вас не было типа Shape с виртуальной функцией name, вы могли бы заставить фрагмент кода «декодировать байты сети» работать с вашей первой функцией, так как компиляторЯ должен заранее знать, какой тип он будет видеть, приходя по сети.

Было предложено пометить этот вопрос как , дубликат этого старого вопроса о шаблонах и наследовании хотя внешне это не тот же вопрос.Как только вы узнаете, что ключевое слово auto в этом контексте означает «это действительно шаблонная функция», вы можете просмотреть другой предложенный вопрос, чтобы получить дополнительные примеры различий между статическим полиморфизмом (с помощью шаблонов).) и полиморфизм времени выполнения (с виртуальными функциями).

...