4

I want to use the Pimpl Idiom but I'm having a problem that one of the member functions is template function so it has to be implemented in a header file.

For example this below works fine of course

//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; }

but is there any way to implement something similar if bar is a template function?

//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; }
};
//...

EDIT

After reading R Sahu's answer and by the looks of all the other comments I figured to do something like it was suggested to me. Explicit template instantiation in a .cpp file seemed like the most clearest option so here is the code if anyone is interested. Thanks to everyone who answered!

//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)
etrusks
  • 363
  • 3
  • 12

1 Answers1

3

You can implement

template<typename T>
void bar(T);

as a member function only if T is limited to a set of known types. In that case, you can use a set of private member functions that are overloaded using a tag struct.

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.
};

Given that the member function template can only be good for a known set of types, you might as well overload them.

class Foo
{
   public:
      Foo();

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

      ~Foo();

   private:
      struct Impl;
      Impl* ptr;    
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 2
    Why not simply use overload or template specification? – ilotXXI May 19 '17 at 20:31
  • That's what I was about to say :) – R Sahu May 19 '17 at 20:33
  • Thanks for the answer man, its a nice solution that I will consider if nothing better comes by. The problem is that I have 4 overloads of this template function and each should support 10 types so its 40 functions. Ill try to figure out some solution with type hiding tomorrow but if I wont come up with anything or better answers wont show up I'll accept your answer :) – etrusks May 19 '17 at 20:37
  • @etrusks, take your time. There is no hurry from my side :) – R Sahu May 19 '17 at 20:43
  • @etrusks Old good (in rare cases, of course) C macros can help you. But not too much... – ilotXXI May 19 '17 at 20:47
  • @ilotXXl Thanks man. I'll try to make some code generator from "advanced metaprogramming in classic C++" if I have to go this way. I don't quite know if I'll succeed but we will see :D – etrusks May 19 '17 at 21:02
  • 2
    @etrusks The good old [X-macro](https://en.wikipedia.org/wiki/X_Macro) in particular. – πάντα ῥεῖ May 19 '17 at 21:19