2017-06-02 74 views
1

我试图使用类模板的pImpl成语,并且当模板参数为void时遇到问题。这里是我的最新的人为的例子:C++ Pimpl成语,类模板和void模板参数

#include <memory> 

template<typename T> class Foo { 
    class     Impl; 
    std::shared_ptr<Impl> pImpl; 
public: 
    Foo() 
     : pImpl{new Impl()} { 
    } 
    void set(T value) { 
     pImpl->set(value); 
    } 
    T get() { 
     return pImpl->get(); 
    } 
}; 

template<typename T> class Foo<T>::Impl { 
    T value; 
public: 
    void set(T value) { 
     this->value = value; // among other things 
    } 
    T get() { 
     return value; // among other things 
    } 
}; 

template<> class Foo<void> { 
    class     Impl; 
    std::shared_ptr<Impl> pImpl; 
public: 
    void set() { 
     pImpl->set(); 
    } 
    void get() { 
     pImpl->get(); 
    } 
}; 

class Foo<void>::Impl { 
public: 
    void set() { 
     // do useful stuff 
    } 
    void get() { 
     // do useful stuff 
    } 
}; 

编译上述结果如下:

$ g++ -dumpversion 
4.8.5 
void_int_template.cpp: In member function ‘void Foo<void>::set()’: 
void_int_template.cpp:34:14: error: invalid use of incomplete type ‘class Foo<void>::Impl’ 
     pImpl->set(); 
      ^
void_int_template.cpp:30:27: error: forward declaration of ‘class Foo<void>::Impl’ 
    class     Impl; 
         ^
void_int_template.cpp: In member function ‘void Foo<void>::get()’: 
void_int_template.cpp:37:14: error: invalid use of incomplete type ‘class Foo<void>::Impl’ 
     pImpl->get(); 
      ^
void_int_template.cpp:30:27: error: forward declaration of ‘class Foo<void>::Impl’ 
    class     Impl; 
         ^

如何我可以专门类模板,以适应void模板参数?

+0

类模板专业化不是对原始模板的“调整” - 它本身就是一个单独的类,除了与原始模板相同的名称外没有其他任何东西。正如你所假设的那样,它不会以某种方式继承其成员。您观察到的直接问题是'template <> class Foo :: Impl {...}'会导致'Foo '的隐式实例化,然后您为此提供明确的专门化。这使程序不合格(通过违反ODR的方式,我认为但不确定)。 –

回答

1

完整的专业化提供了一个备用定义,这意味着您必须重新定义一切。

template<> class Foo<void> { 
    class     Impl; 
    std::shared_ptr<Impl> pImpl; 
public: 
    Foo(); 
    void set(); 
    void get(); 
}; 

class Foo<void>::Impl { 
public: 
    void set() { 
    } 
    void get() { 
    } 
}; 

// these need to be inline iff it's in your header file 
/* inline */ Foo<void>::Foo() : pImpl(new Impl) {} 
/* inline */ void Foo<void>::set() { pImpl->set(); } 
/* inline */ void Foo<void>::get() { pImpl->get(); } 
+0

这似乎应该工作,但似乎没有。我根据您的建议修改了我的示例,现在在模板类“Foo ”中出现“前向声明”错误。看到新的例子。 –

+0

@SteveEmmerson这就是为什么我在我的答案中定义了类外的成员函数... –

+0

我低估了,因为这看起来不对,但我越理解这个问题,这个答案就越正确。你能澄清一点吗? (问题是'Foo :: set'被实例化,因为它不依赖于模板类型,因此您需要推迟方法实例化) –