How to create a private static constant string using the pimpl idiom

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.

+5
source share
2 answers
 #include <memory> class Widget { public: Widget(); ~Widget(); private: class Impl; std::unique_ptr<Impl> pimpl; }; /*** cpp ***/ #include <string> class Widget::Impl { public: static const std::string TEST; Impl() { }; ~Impl() { }; }; const std::string Widget::Impl::TEST = "test"; Widget::Widget() : pimpl(new Impl()) { } Widget::~Widget() { } 

You might want to make a static TEST function that returns const std::string& . This will allow you to define it inline.

+3
source

You can also replace const with constexpr in your example and compile.

 class Widget::Impl { public: static constexpr std::string TEST = "test"; // constexpr here Impl() { }; ~Impl() { }; }; 

Update:

Well, it seems like I was wrong ... I always store the raw string when I want constants.

 class Widget::Impl { public: static constexpr char * const TEST = "test"; }; 

Depending on the usage pattern, this may or may not be appropriate. If not, then define the variable as described in another answer.

+1
source

Source: https://habr.com/ru/post/1270615/


All Articles