带有模板成员函数的 Pimpl Idiom

Pimpl Idiom with template member function

我想使用 Pimpl Idiom,但我遇到了一个问题,其中一个成员函数是模板函数,因此它必须在头文件中实现。

例如下面这个当然可以正常工作

//Foo.h
class Foo{
    struct Impl;
    Impl* ptr;
public:
    Foo();
    void bar(int);
    ~Foo();
};


//Foo.cpp
struct Foo::Impl{
    void bar(int i){ std::cout << "i = " << i << std::endl; }
};

Foo::Foo() : ptr{new Impl}{}
void Foo::bar(int i){ ptr->bar(i); }
Foo::~Foo(){ delete ptr; }

但是如果 bar 是一个模板函数,有什么方法可以实现类似的东西吗?

//Foo.h
class Foo{
    struct Impl;
    Impl* ptr;
public:
    Foo();
    template<typename T>
    void bar(T);
    ~Foo();
};

template<typename T>
void Foo::bar(T val)
{
    /*has to be implemented in a header but I cant call member function 
    on an incomplete type*/
    ptr->bar(val); //error
}

//Foo.cpp
struct Foo::Impl{
    template<typename T>
    void bar(T val){ std::cout << "val = " << val << std::endl; }
};
//...

编辑

在阅读了 R Sahu 的回答和所有其他评论之后,我想按照建议的那样做一些事情。 .cpp 文件中的显式模板实例化似乎是最清晰的选项,所以如果有人感兴趣,这里是代码。感谢所有回答的人!

//Foo.h
class Foo{
    struct Impl;
    Impl* ptr;
public:
    Foo();
    template<typename T>
    void bar(T);
    ~Foo();
};


//Foo.cpp
struct Foo::Impl{
    template<typename T>
    void bar(T val){ std::cout << "val = " << val << std::endl; }
};

template<typename T>
void Foo::bar(T val)
{
    ptr->bar(val);
}

Foo::Foo() : ptr{ new Impl}{}
Foo::~Foo(){ delete ptr; }

#define instantiate_template_function(type)\
    template void Foo::bar(type);

instantiate_template_function(int)
instantiate_template_function(double)
instantiate_template_function(char)
instantiate_template_function(float)
instantiate_template_function(long long)

你可以实施

template<typename T>
void bar(T);

仅当 T 仅限于一组已知类型时才作为成员函数。在这种情况下,您可以使用一组 private 成员函数,这些成员函数使用标记结构重载。

class Foo
{
   template <typename T> struct Tag {};

   public:
      Foo();
      template<typename T>
         void bar(T val)
         {
            bar(val, Tag<T>{});
         }
      ~Foo();

   private:
      struct Impl;
      Impl* ptr;

      void bar(int val, Tag<int> tag);
      void bar(double val, Tag<double> tag);
      // etc.
      // Implement them in the .cpp file.
};

鉴于成员函数模板只能适用于一组已知的类型,您不妨重载它们。

class Foo
{
   public:
      Foo();

      void bar(int val);
      void bar(double val);
      // etc.

      ~Foo();

   private:
      struct Impl;
      Impl* ptr;    
};