2015-02-28 8 views
3

Приносим извинения за большой объем кода, необходимый для демонстрации проблемы. У меня проблема с использованием идиомы pimpl с std :: unique_ptr. В частности, проблема возникает, когда один класс (который имеет реализацию pimpl'ed) используется как данные элемента в другом составном классе с реализацией pimpl'ed.C++ Pimpl Idiom Неполный тип с использованием std :: unique_ptr

Большинство ответов я смог найти дело с отсутствием explicit destructor declaration, но, как вы можете видеть здесь, я объявил и определил деструкторы.

Что не так с этим кодом, и может ли он быть изменен для компиляции без изменения дизайна?

Примечание: ошибка возникает в определении SomeComposite :: getValue() и что компилятор не может видеть ошибку до момента компиляции. Ошибка встречается в памяти.h, и сообщение Недопустимое применение параметра sizeof для неполного типа pimplproblem :: SomeInt :: impl '.

SomeInt.h

#pragma once 
#include <iostream> 
#include <memory> 

namespace pimplproblem 
{ 
    class SomeInt 
    { 

    public: 
     explicit SomeInt(int value); 
     SomeInt(const SomeInt& other); // copy 
     SomeInt(SomeInt&& other) = default; // move 
     virtual ~SomeInt(); 
     SomeInt& operator=(const SomeInt& other); // assign 
     SomeInt& operator=(SomeInt&& other) = default; // move assign 
     int getValue() const; 

    private: 
     class impl; 
     std::unique_ptr<impl> myImpl; 
    }; 
} 

SomeInt.cpp

#include "SomeInt.h" 

namespace pimplproblem 
{ 
    class SomeInt::impl 
    { 
    public: 
     impl(int value) 
     :myValue(value) 
     {} 

     int getValue() const 
     { 
      return myValue; 
     } 
    private: 
     int myValue; 
    }; 

    SomeInt::SomeInt(int value) 
    :myImpl(new impl(value)) 
    {} 

    SomeInt::SomeInt(const SomeInt& other) 
    :myImpl(new impl(other.getValue())) 
    {} 

    SomeInt::~SomeInt() 
    {} 

    SomeInt& SomeInt::operator=(const SomeInt& other) 
    { 
     myImpl = std::unique_ptr<impl>(new impl(other.getValue())); 
     return *this; 
    } 

    int SomeInt::getValue() const 
    { 
     return myImpl->getValue(); 
    } 
} 

SomeComposite.h

#pragma once 
#include <iostream> 
#include <memory> 
#include "SomeInt.h" 

namespace pimplproblem 
{ 
    class SomeComposite 
    { 

    public: 
     explicit SomeComposite(const SomeInt& value); 
     SomeComposite(const SomeComposite& other); // copy 
     SomeComposite(SomeComposite&& other) = default; // move 
     virtual ~SomeComposite(); 
     SomeComposite& operator=(const SomeComposite& other); // assign 
     SomeComposite& operator=(SomeComposite&& other) = default; // move assign 
     SomeInt getValue() const; 

    private: 
     class impl; 
     std::unique_ptr<impl> myImpl; 
    }; 
} 

SomeComposite.cpp

#include "SomeComposite.h" 

namespace pimplproblem 
{ 
    class SomeComposite::impl 
    { 
    public: 
     impl(const SomeInt& value) 
     :myValue(value) 
     {} 

     SomeInt getValue() const 
     { 
      return myValue; 
     } 
    private: 
     SomeInt myValue; 
    }; 

    SomeComposite::SomeComposite(const SomeInt& value) 
    :myImpl(new impl(value)) 
    {} 

    SomeComposite::SomeComposite(const SomeComposite& other) 
    :myImpl(new impl(other.getValue())) 
    {} 

    SomeComposite::~SomeComposite() 
    {} 

    SomeComposite& SomeComposite::operator=(const SomeComposite& other) 
    { 
     myImpl = std::unique_ptr<impl>(new impl(other.getValue())); 
     return *this; 
    } 

    SomeInt SomeComposite::getValue() const 
    { 
     return myImpl->getValue(); 
    } 
} 
+0

см. Также http://stackoverflow.com/q/8595471/103167 –

ответ

5

Вы не можете использовать дефолтные конструктор и оператор присваивания (такие как SomeInt(SomeInt&& other) = default;), объявленные в заголовочном файле с классами Pimpl, потому что реализация по умолчанию являются встроенной, и в момент декларации SomeInt ' s SomeInt::impl является неполным, поэтому unique_ptr жалуется. Вы должны объявлять и определять вне строки (то есть в файле реализации) все специальные функции-члены самостоятельно.

То есть, изменить SomeInt и SomeComposite декларации следующим образом:

// SomeInt.h 
SomeInt(SomeInt&& other); // move 
SomeInt& operator=(SomeInt&& other); // move assign 

// SomeInt.cpp 
// after definition of SomeInt::impl 
SomeInt::SomeInt(SomeInt&& other) = default; 
SomeInt& operator=(SomeInt&& other) = default; 

Другой вариант создать свой собственный указатель Pimpl, как это было предложено в this answer.

+1

Этот ответ http://stackoverflow.com/a/11212403/2779792 привел меня в заблуждение. Кажется, что ответ неверен, поскольку он показывает завершенное объявление класса impl в заголовочном файле (?), Которое побеждает цель pimpl. Затем ответ будет использоваться по умолчанию. Возможно, я не понял Q/A, потому что моя ситуация, по-видимому, отличается. –

+1

@MatthewJamesBriggs самый высокий рейтинг комментария к этому вопросу предлагает прочитать [GOTW 100] (http://herbsutter.com/gotw/_100/), который гораздо полезнее, чем ответ. Я также обновил свой ответ с более конкретными инструкциями, что делать. –

+0

@MatthewJamesBriggs: Этот ответ не ошибается, но он делает что-то по-другому и вообще не предоставляет компиляционный брандмауэр. Обратите внимание, что в этом ответе тело 'impl' находится в файле заголовка, а не в файле реализации, как ваш. В нем также рассматриваются проблемы, возникающие при перемещении определения 'impl'. –

 Смежные вопросы

  • Нет связанных вопросов^_^