Форвардные объявления, которые включают std :: vector и т. Д.

Я много использовал форвардные объявления; они помогают избежать множества #include, улучшить время компиляции и многое другое. Но что, если я хочу объявить класс в стандартной библиотеке?

// Prototype of my function - i don't want to include <vector> to declare it!
int DoStuff(const std::vector<int>& thingies);

Я слышал, что пересылать объявление std::vector запрещено / невозможно. Теперь этот ответ на несвязанный вопрос предлагает переписать мой код следующим образом:

stuff.h

class VectorOfNumbers; // this class acts like std::vector<int>
int DoStuff(const VectorOfNumbers& thingies);

stuff.cpp

// Implementation, in some other file
#include <vector>
class VectorOfNumbers: public std::vector<int>
{
    // Define the constructors - annoying in C++03, easy in C++11
};

int DoStuff(const VectorOfNumbers& thingies)
{
    ...
}

Теперь, если я использую VectorOfNumbers вместо std::vector<int> во всех контекстах моего проекта, все будет хорошо, и мне больше не нужно #include <vector> в моих файлах заголовков!

Есть ли у этой техники серьезные недостатки? Может ли выгода от возможности продвигать-декларировать vector перевесить их?


person anatolyg    schedule 25.10.2011    source источник


Ответы (5)


Причина, по которой я не стал бы этого делать:

const std::vector<int>& a = a_3rd_party_lib::get_data(); // this type is out of your control
DoStuff(a); // you cannot pass it to this function! 
person Yakov Galka    schedule 25.10.2011
comment
Разве VectorOfNumbers не может просто тривиально предоставить ctor преобразования из базового типа std :: vector? - person Martin Ba; 17.02.2012
comment
@Martin: Он скопировал бы все это. Вы же этого не хотите? - person Yakov Galka; 17.02.2012

Если вы когда-нибудь удалили VectorOfNumbers как std::vector<int> (и поскольку вы использовали публичное наследование, это преобразование является неявным), вы попали в область неопределенного поведения. Вероятно, это произойдет случайно, с большей вероятностью, чем можно было бы подозревать.

Я никогда лично не замечал значительного замедления компиляции из-за простого включения vector там, где это необходимо, но если вы действительно хотите изолировать включение, используйте интерфейс клиентского API, который не знает о базовом типе контейнера (vector), и вставьте vector include в единый исходный файл.

person Mark B    schedule 25.10.2011

Вы включаете <vector> в свой заголовочный файл. Заголовок <vector> будет создан для предотвращения множественных включений, поэтому вы просто включаете его везде, где вам это нужно.

person ObscureRobot    schedule 25.10.2011
comment
Как это отвечает на вопрос ОП? - person ildjarn; 26.10.2011

Это хорошо работает для интерфейса класса, но не для реализации. Если в вашем классе есть какие-либо vector члены, вы должны #include <vector>, иначе определение класса не будет компилироваться.

person Mark Ransom    schedule 25.10.2011

Вместо наследования можно использовать композицию:

// Implementation, in some other file
#include <vector>
class VectorOfNumbers
{
    public:

    std::vector<int>& container;

    VectorOfNumbers(std::vector<int>& in_container)
        : container(in_container)
    {
    }
};

int DoStuff(const VectorOfNumbers& thingies)
{
    std::sort(thingies.container.begin(), thingies.container.end());
    // ...
}

Обратной стороной является дополнительное имя переменной при каждом доступе.

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

По сути, просто делаем обертку для вашего вектора. Это похоже на облегченную версию PImpl (мы заботимся только о том, чтобы избежать зависимости заголовков, поэтому нам не нужна полная развязка указателя). Это позволяет избежать проблем, поднятых Mark B и ybungalobill.

Но я не думаю, что оно того стоит.

person idbrii    schedule 01.09.2017