Background
I am learning how to implement the pimpl idiom using the new C ++ 11 method described by Herb Sutter on this page: https://herbsutter.com/gotw/_100/
I am trying to modify this example by adding a member variable to a private implementation, in particular std :: string (although a char * has the same problem).
Problem
This seems impossible due to the use of a static const of non-integer type. Initialization within a class can only be performed for integral types, but since it is static, it also cannot be initialized in the constructor.
The solution to this problem is to declare a private variable in the header file and initialize it in the implementation, as shown below: C ++ static constant string (class member)
However, this solution does not work for me because it violates the encapsulation that I am trying to achieve through the pimpl idiom.
Question
How can I hide a non-integer static constant variable inside a hidden inner class when using the pimpl idiom?
Example
Here is a simple (wrong) example that I could come up with a demonstration of the problem:
Widget.h:
#ifndef WIDGET_H_ #define WIDGET_H_ #include <memory> class Widget { public: Widget(); ~Widget(); private: class Impl; std::unique_ptr<Impl> pimpl; }; #endif
Widget.cpp:
#include "Widget.h" #include <string> class Widget::Impl { public: static const std::string TEST = "test"; Impl() { }; ~Impl() { }; }; Widget::Widget() : pimpl(new Impl()) { } Widget::~Widget() { }
Compilation command:
g++ -std=c++11 -Wall -c -o Widget.o ./Widget.cpp
Please note that this example is not compiled because the TEST variable cannot be assigned when declared because it is not an integral type; however, since it is static, this is required. This, apparently, means that it is impossible.
I looked for previous questions / answers to all this in the afternoon, but could not find suggestions offering a solution that preserves the property of hiding the information of the pimpl idiom.
Observation solutions:
In my example above, I tried to assign a TEST value in the declaration of the Impl class, which is inside Widget.cpp, and not its own header file. The definition of Impl is also contained in Widget.cpp, and I believe that this has caused my confusion.
By simply moving the TEST assignment outside the Impl declaration (but still in the Widget / Impl definition), the problem seems to be resolved.
In both of the examples below, TEST solutions can be obtained from within Widget using
pimpl->TEST
Trying to assign another line to TEST, i.e.
pimpl->TEST = "changed"
leads to a compiler error (as it should be). In addition, attempting to access pimpl-> TEST from outside Widget also results in a compiler error, because pimpl is declared private to Widget.
So, now TEST is a constant string that only Widget can access, is not listed in the public header, and one copy is common to all Widget instances exactly as desired.
Example solution (char *):
When using char *, pay attention to adding another const keyword; this was necessary to prevent the TEST from changing to point to another string literal.
Widget.cpp:
#include "Widget.h" #include <stdio.h> class Widget::Impl { public: static const char *const TEST; Impl() { }; ~Impl() { }; }; const char *const (Widget::Impl::TEST) = "test"; Widget::Widget() : pimpl(new Widget::Impl()) { } Widget::~Widget() { }
Solution example (string):
Widget.cpp:
#include "Widget.h" #include <string> class Widget::Impl { public: static const std::string TEST; Impl() { }; ~Impl() { }; }; const std::string Widget::Impl::TEST = "test"; Widget::Widget() : pimpl(new Widget::Impl()) { } Widget::~Widget() { }
Update:
Now I understand that the solution to this problem is completely unrelated to the pimpl idiom and is simply a standard C ++ method for determining static constants. I am used to other languages, such as Java, where constants must be defined at the time of their declaration, so my inexperience with C ++ prevented me from understanding this from the beginning. I hope this avoids confusion on two topics.