A locale object is like a container or a map, to be more precise, but it is indexed by type at compile time. The indexing operator, therefore, is not the subscript operator operator[](), but rather the template operator <>. Access to the facet objects of a locale object is via two member function templates, use_facet() and has_facet():
template<class Facet> const Facet& use_facet(const locale&); template<class Facet> bool has_facet(const locale&);
The code below demonstrates how they are used. It is an example of the ctype facet's usage; all upper case letters of a string read from the standard input stream are converted to lower case letters and written to the standard output stream.
std::string in; std::cin >> in; if (std::has_facet< ctype<char> >(std::locale())) //1 { std::cout << use_facet<ctype<char> >(std::locale()) //2 .tolower(in.begin(),in.end()); //3 }
//1 | In the call to has_facet<...>(), the template argument chooses a base facet class. If no facet of this type is present in a locale object, has_facet returns false. |
//2 | The function template use_facet<...>() returns a reference to the locale's facet object of the specified base facet type. As locale objects are immutable, the reference stays valid throughout the lifetime of the locale object. If no facet of the specified type is present in the locale, use_facet throws a runtime_error exception. |
//3 | The facet object's member function tolower() is called. It has the functionality of the C function tolower(); it converts all upper case letters in the string into lower case letters. |
In this example, the call to has_facet is actually unnecessary because ctype<char> is a standard facet. Every locale always contains a complete complement of the standard facets, so has_facet<ctype<char> > always returns true. However, a call to has_facet() can be useful when you are dealing with nonstandard facets, such as the mythical facet described earlier in this chapter, which may not be present in every locale.
When you are coding use_facet and has_facet calls, it is important that the facet type you specify as the template parameter is a base facet type, and not a derived facet type. The following is an error:
locale loc; const std::numpunct_byname<char>& np = // Error, use_facet use_facet<std::numpunct_byname<char> >(loc); // instantiated on // a non-base facet // type
Depending on the facet type, and on your compiler, this will probably cause a compile-time error. If it does not, it may result in unpredictable runtime behavior. The use_facet call returns the facet that occupies the slot for the type std::numpunct_byname<char>. But in fact, as described earlier, this is the same as the slot for the base facet type, std::numpunct<char>. So the above code may cause np to be initialized to a reference to an object that is not, in fact, of type std::numpunct_byname<char>.
To avoid errors like this, make sure that you only instantiate use_facet and has_facet on base facet types, that is, on facet types that contain a static locale::id member.
locale loc; const numpunct<char>& np = // Correct use_facet<numpunct<char> >(loc);