Ошибка компоновщика при реализации языка pimpl - PullRequest
0 голосов
/ 01 октября 2011

Отредактировано для провайдера немного большей ясности.Извиняюсь за то, что запутал всех.

Это под Windows.

У меня есть статическая библиотека, которая реализует класс с использованием идиомы pimpl.Заголовок pimpl не только используется потребляющим кодом, но и связывается со статической библиотекой.Тем не менее, когда я компилирую код потребления (.exe), компоновщик жалуется на неразрешенные внешние компоненты класса реализации , которые предположительно скрывает заголовок pimpl.

Как это может быть возможно?

// Foo.lib

// Foo.h

class FooImpl;

class Foo
{
    std::shared_ptr<FooImpl> pimpl_;    
public:
    Foo();
};

// Foo.cpp

#include "Foo.h"
#include "FooImpl.h"

Foo::Foo() : pimpl_(new FooImpl())
{
}

// This is how its used in Bar.exe
// Bar.exe links against Foo.lib

// Bar.h

#include "Foo.h"

class Bar
{
    Foo access_foo_;
};

// Bar.cpp

#include "Bar.h"

Когда я компилирую / связываю Bar.exe, компоновщик жалуется, что не может разрешить FooImpl.Я забыл, что именно он сказал, поскольку у меня сейчас нет доступа к рабочему ПК, но в этом суть.Ошибка не имела смысла для меня, потому что цель перехода по маршруту pimpl состояла в том, чтобы Bar.exe не приходилось беспокоиться о FooImpl.

Точная ошибка выглядит следующим образом:

1> Foo.lib (Foo.obj): ошибка LNK2019: неразрешенный внешний символ «public: __thiscall FooImpl :: FooImpl (void)» (?? 0FooImpl @@ QAE @ XZ), на который есть ссылка в функции «public: __thiscall Foo ::Foo (void) "(?? 0Foo @@ QAE @ XZ)

Ответы [ 2 ]

2 голосов
/ 01 октября 2011

1> Foo.lib (Foo.obj): ошибка LNK2019: неразрешенный внешний символ «public: __thiscall FooImpl :: FooImpl (void)» (?? 0FooImpl @@ QAE @ XZ), на который есть ссылка в функции «public: __thiscall Foo :: Foo (void) "(?? 0Foo @@ QAE @ XZ)

Компоновщик жалуется, что не может найти определение FooImpl::FooImpl(). Вы вызываете конструктор здесь -

Foo::Foo() : pimpl_(new FooImpl())
                    // ^^^^^^^^^ Invoking the default constructor.

Вы только что предоставили объявление конструктора по умолчанию, но это не определение .

2 голосов
/ 01 октября 2011

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

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


После просмотра ваших изменений проблема в том, что конструктору Foo :: Foo необходим доступ к конструктору FooImpl :: FooImpl, но компоновщик не знает, где его найти. Когда компоновщик собирает библиотеку, он не пытается разрешить все ссылки в это время, потому что он может зависеть от еще одной библиотеки. Единственный раз, когда все должно быть решено, это когда он собирает исполняемый файл. Поэтому, хотя Bar и не нужно напрямую знать о FooImpl, он все равно зависит от него.

Вы можете решить эту проблему одним из двух способов: либо экспортировать FooImpl из библиотеки вместе с Foo, либо убедиться, что Foo имеет доступ к FooImpl во время компиляции, поместив их обоих в Foo.cpp с FooImpl, предшествующим Foo.

...