Совместим ли pimpl с анонимными пространствами имен? - PullRequest
5 голосов
/ 21 апреля 2011

Я пытаюсь использовать шаблон pimpl и определить класс реализации в анонимном пространстве имен.Возможно ли это в C ++?Моя неудачная попытка описана ниже.

Можно ли это исправить, не перемещая реализацию в пространство имен с именем (или глобальным)?

class MyCalculatorImplementation;

class MyCalculator
{
public:
    MyCalculator();
    int CalculateStuff(int);

private:
    MyCalculatorImplementation* pimpl;
};

namespace // If i omit the namespace, everything is OK
{
    class MyCalculatorImplementation
    {
    public:
        int Calculate(int input)
        {
            // Insert some complicated calculation here
        }

    private:
        int state[100];
    };
}

// error C2872: 'MyCalculatorImplementation' : ambiguous symbol
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation)
{
}

int MyCalculator::CalculateStuff(int x)
{
    return pimpl->Calculate(x);
}

Ответы [ 4 ]

6 голосов
/ 21 апреля 2011

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

// .hpp
struct Foo {
    Foo();
    // ...
private:
    struct FooImpl;
    boost::scoped_ptr<FooImpl> pimpl;
};

// .cpp
struct Foo::FooImpl {
    FooImpl();
    // ...
};

Foo::Foo() : pimpl(new FooImpl) { }
2 голосов
/ 11 июля 2011

Да.Для этого есть обходной путь.Объявите указатель в заголовочном файле как void *, затем используйте переинтерпретацию в вашем файле реализации.

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

См. Пример реализации ниже:

class MyCalculator 
{
public:
    MyCalculator();
    int CalculateStuff(int);

private:
    void* pimpl;
};

namespace // If i omit the namespace, everything is OK
{
    class MyCalculatorImplementation
    {
    public:
        int Calculate(int input)
        {
            // Insert some complicated calculation here
        }

    private:
        int state[100];
    };
}

MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation)
{
}

MyCalaculator::~MyCalaculator() 
{
    // don't forget to cast back for destruction!
    delete reinterpret_cast<MyCalculatorImplementation*>(pimpl);
}

int MyCalculator::CalculateStuff(int x)
{
    return reinterpret_cast<MyCalculatorImplementation*>(pimpl)->Calculate(x);
}
1 голос
/ 21 апреля 2011

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

// header
class MyCalculatorInterface;

class MyCalculator{
   ...
   MyCalculatorInterface* pimpl;
};



//module
class MyCalculatorInterface{
public:
    virtual int Calculate(int) = 0;
};

int MyCalculator::CalculateStuff(int x)
{
    return pimpl->Calculate(x);
}

namespace {
    class MyCalculatorImplementation: public MyCalculatorInterface {
        ...
    };
}

// Only the ctor needs to know about MyCalculatorImplementation
// in order to make a new one.
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation)
{
}
1 голос
/ 21 апреля 2011

Нет, вы не можете этого сделать.Вы должны заранее объявить класс Pimpl:

class MyCalculatorImplementation;

, и это объявит класс.Если вы затем поместите определение в безымянное пространство имен, вы создаете другой класс (anonymous namespace)::MyCalculatorImplementation, который не имеет ничего общего с ::MyCalculatorImplementation.

Если это было любое другое пространство имен NS, вы можете изменитьforward-декларация для включения пространства имен:

namespace NS {
    class MyCalculatorImplementation;
}

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

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

...