Inability to instantiate a standard facet

Naturally it isn't possible to instantiate a facet, since a facet doesn't encapsulate any specific functionality, instead leaving that to specific classes derived from facet, such as collate<C>, etc.

Ie, the facet class is designed to be a base class.

For this reason, to prevent a facet being instantiated directly, its constructor is protected and destructor is virtual (to ensure proper destruction of derived-class objects):

1
2
3
4
5
6
7
8
9
10
class locale::facet
{
protected:
   explicit facet(size_t refs = 0);

   virtual ~facet();

   facet(const facet&) = delete;
   void operator=(const facet&) = delete;
};



However, now consider the standard facet collate<C>, which is derived from facet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<typename C>
class collate : public locale::facet
{
public:
   /// ...

protected:
   ~collate();		/// note: protected destructor

   virtual int do_compare(
                  const C* b, const C* e,
                  const C* b2, const C* e2) const;

   virtual string_type do_transform(
                          const C* b,
                          const C* e) const;

   virtual long do_hash(const C* b,
                        const C* e) const;
};


Notice that here too, the destructor is protected. Therefore you won't be able to instantiate a collate<char>. If you try to do so, you will get an error:

 
collate<char> colc;


The error is:

error: 'std::collate<_CharT>::~collate() [with _CharT = char]' is protected|


Yet, the collate<C> template class's protected members (do_compare(), do_transform() and do_hash()) all contain encapsulated functionality and there was no need for the standard facet collate<C> to have been declared with a protected dtor.


For this reason, to create a collate<C>, we need to be first derive a class from collate<C>, which can then be instantiated!

1
2
3
4
5
6
7
8
9
10
11
template<typename C>
class My_collate : public collate<C>
{
public:
    explicit My_collate(size_t r = 0) :
        collate<C> {r}
    {
    }
};

My_collate<char> mcolc;


This successfuly creates a My_collate<char> (which is a collate<char> by derivation).

Just to prove that My_collate<char> encapsulates inherited functionality, I have successfully tested it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void print(const string& s1, const string& s2,
           const int& rslt)
{
    string srslt {};

    switch(rslt)
    {
    case 0:
        srslt = "equal";
        break;

    case 1:
        srslt = "s1 > s2";
        break;


    case -1:
        srslt = "s1 < s2";
        break;
    }

    cout << "comparison of " << s1 << " and " << s2
         << " using the mcolc facet : "
         << srslt << endl;
}


void test(const string& s1, const string& s2)
{
    /// since compare() operates on char[]s
    const char* s1b = s1.data();        /// start of data
    const char* s1e = s1b + s1.size();  /// end of data
    const char* s2b = s2.data();        /// start of data
    const char* s2e = s2b + s2.size();  /// end of data

    int rslt = mcolc.compare(s1b, s1e, s2b, s2e);

    /// display results
    print(s1, s2, rslt);
}


int main()
{
    test("Hello", "Hello");
    test("Hello", "hello");
    test("hello", "Hello");
}


https://ideone.com/D6zrbr

Here's the output:

comparison of Hello and Hello using the mcolc facet : equal
comparison of Hello and hello using the mcolc facet : s1 < s2
comparison of hello and Hello using the mcolc facet : s1 > s2


So, my question is: Why do standard facets such as collate<C> have a protected destructor, so as to prevent direct instantiation, when they already encapsulate the full required functionality?

Thanks.
Last edited on
> Why do standard facets such as collate<C> have a protected destructor

The base class std::facet provides facilities for reference counting, and std::locale manages the life-time of its facet objects. The internal mechanism for managing this reference count is implementation defined. Facets are typically instantiated with a new expression and programmers are not required (or expected) to manage the life-time of such facet objects.

Whenever a facet is added to a locale, the locale increments the reference count in the facet (through an implementation-specific mechanism). Whenever a locale is destructed or modified, it decrements the reference count in each facet it no longer implements. Whenever a facet's reference count becomes zero, the locale performs delete static_cast<std::locale::facet*>(f) where f is the pointer to the facet.
http://en.cppreference.com/w/cpp/locale/locale/facet


Almost always, user-created facets are used in conjunction with a locale; for example:
1
2
3
4
std::locale my_loc( std::locale(), new std::collate<char> ) ;

// or somewhat more realistically: 
stm.imbue( std::locale( stm.getloc(), new std::collate_byname<char>("POSIX") ) ) ;

a public destructor is not required for these constructs.
SSteven wrote:
Why do standard facets such as collate<C> have a protected destructor

This was actually raised as a defect report http://wg21.link/lwg721 (in context of the use of facets with wstring_convert), and that was closed as "not a defect", with the rather unsatisfying explanation "codecvt isn't intended for beginning programmers. This is a regrettable consequence of the original design of the facet."

In practice, when I need a facet that's not controlled by a locale, I wrap it like this:

1
2
3
4
5
6
template<class Facet>
struct deletable_facet : Facet
{
    using Facet::Facet; // inherit constructors
    ~deletable_facet() {}
};


as shown in action here: http://en.cppreference.com/w/cpp/locale/wstring_convert/wstring_convert#Example
Last edited on
1) I think Borges' reply has all to do with the facet class, but not with a standard-library derived class of facet, such as collate<C>.

After all, facet doesn't encapsulate functionality, but collate<C> does, as I had pointed out in my original post.

Therefore, it is valid for the facet class to have a protected destructor. But my original query was why it was necessary for a derived class of facet to do the same, since the latter does incorporate functionality, and no virtual function needs to be implemented.

2) I think Cubbi has hit the nail on the head. The only plausible explanation is that this action was deliberate on the part of the C++ standard setters - to prevent novices from instantiating a facet.

Indeed, Stroustrup writes ["The C++ Programming Language, 4th Ed, pg 1125]:


Standard facets often depend on each other. For example, the num_put facet depends on the numpunct facet. It is essential for one to have a detailed knowledge of individual facets, to be able to successfully mix and match facets or add new versions of the standard facets.

In other words, beyond the simple operations (such as using imbue() for iostreams and collate for sort(), the locale mechanisms aren’t meant to be directly used by novices.

For an extensive discussion of locales, refer “Standard C++ IOStreams and Locales”, Angelika Langer, ©2000.


3) Regarding Cubbi's code for deletable_facet, if the Facet type passed as template arg has a ctor with a size_t arg with value 0, the created deletable_facet would have to be managed by a locale, rather than deleted manually.

Hence, perhaps My_facet might be a better name for the struct, than deletable_facet.
Topic archived. No new replies allowed.