4

I am trying to define a full specialization of std::basic_string< char, char_traits<char>, allocator<char> > which is typedef'd (in g++) by the <string> header.

The problem is, if I include <string> first, g++ sees the typedef as an instantiation of basic_string and gives me errors. If I do my specialization first then I have no issues.

I should be able to define my specialization after <string> is included. What do I have to do to be able to do that?

My Code:

#include <bits/localefwd.h>

//#include <string> // <- uncommenting this line causes compilation to fail

namespace std {
template<>
class basic_string< char, char_traits<char>, allocator<char> >
{
public:
    int blah() { return 42; }
    size_t size() { return 0; }
    const char *c_str() { return ""; }
    void reserve(int) {}
    void clear() {}
};
}

#include <string>
#include <iostream>

int main() {
    std::cout << std::string().blah() << std::endl;
}

The above code works fine. But, if I uncomment the first #include <string> line, I get the following compiler errors:

blah.cpp:7: error: specialization of ‘std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ after instantiation
blah.cpp:7: error: redefinition of ‘class std::basic_string<char, std::char_traits<char>, std::allocator<char> >’
/usr/include/c++/4.4/bits/stringfwd.h:52: error: previous definition of ‘class std::basic_string<char, std::char_traits<char>, std::allocator<char> >’
blah.cpp: In function ‘int main()’:
blah.cpp:22: error: ‘class std::string’ has no member named ‘blah’

Line 52 of /usr/include/c++/4.4/bits/stringfwd.h:

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_string;

As far as I know this is just a forward delcaration of the template, NOT an instantiation as g++ claims.

Line 56 of /usr/include/c++/4.4/bits/stringfwd.h:

   typedef basic_string<char>    string;

As far as I know this is just a typedef, NOT an instantiation either.

So why are these lines conflicting with my code? What can I do to fix this other than ensuring that my code is always included before <string>?

SoapBox
  • 20,457
  • 3
  • 51
  • 87
  • Give us the bigger picture so you can avoid this disaster :/ – GManNickG Apr 23 '10 at 21:16
  • 1
    By the way: "As far as I know this is just a forward delcaration of the template" - it's not. A forward declaration of a template that would be instantiated elsewhere is achieved by using `extern template` (which is a non-standard, but widely accepted, extension in C++03, and standard in C++0x). Without `extern`, it's an "explicit template instantiation", as described in ISO C++03 14.7.2 – Pavel Minaev Apr 23 '10 at 21:20
  • @GMan: What I really want is to toy with different string strategies (like COW). Obviously I can just make my own class but I was hoping since it is intended to be a drop-in replacement for std::string that it literally drop in and replace it, without even requiring any code modifications other than an extra include. – SoapBox Apr 23 '10 at 21:25
  • @Soap: I don't think there's any other way to really get what you want without reimplementing `basic_string` in ``yourself. :/ – GManNickG Apr 23 '10 at 21:55
  • 1
    @GMan, Soap: Take a gander at this: http://www.drdobbs.com/cpp/184403784;jsessionid=FENWJOWHTUPFRQE1GHOSKH4ATMY32JVN – John Dibling Apr 24 '10 at 03:41
  • 1
    @Pavel, not true. What he showed is actually a forward declaration of a template. What you show ("extern template") is an explicit instantiation declaration (and going to be in C++0x). Forward declarations are not meant to stop instantiations, but are meant to introduce names before-the-fact. That's what his showed code does, and that's what "extern template" does *not*. – Johannes Schaub - litb Apr 25 '10 at 20:21
  • I didn't say that "forward declarations" stop instantiations. I said that an instantiation will happen at the moment of said declaration, if the definition of the template is in scope - so later attempt to specialize it for the same types will inevitably fail. The difference with "extern" is that it will prevent template from being implicitly instantiated upon use for a given set of template parameters. – Pavel Minaev Apr 26 '10 at 03:49
  • Anyway, can you explain 14.7.2 [temp.explicit] then? "14.7.2 Explicit instantiation 1 A class, a function or member template specialization can be explicitly instantiated from its template. A member function, member class or static data member of a class template can be explicitly instantiated from the member definition associated with its class template. 2 The syntax for explicit instantiation is: `explicit-instantiation: template declaration`". And the example given is precisely like the one in the question, e.g. `template class Array;` – Pavel Minaev Apr 26 '10 at 03:50
  • @Pavel, not at all. In the question, it looks exactly like a class template definition without its body. In that explicit instantiation, however, the template parameter list is completely omitted. It just says `template`. So it's a bit like the difference between `class foo;` and `foo bar;`. – Johannes Schaub - litb Apr 26 '10 at 05:11

2 Answers2

9

You are only allowed to specialize a standard library if the specialization depends on a user-defined name with external linkage. char doesn't meet this requirement and you are getting undefined behaviour.

This is specified in 17.4.3.1 [lib.reserver.names]/1.

The particular error that you are getting is because your implementation already instantiates the template that you are trying to specialize and, if you provide a specialization for any template, it must be before the template is ever instantiated with the parameters for which you want to provide the specialization.

14.7.3 [temp.expl.spec]/6

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 2
    ISO C++03 17.4.3.1 "Reserved names": It is undefined for a C + + program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library template results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the standard library requirements for the original template.) – Pavel Minaev Apr 23 '10 at 21:14
0

As a general rule, you should not define or specialize anything other than a traits class in namespace std, and you certainly should not attempt to change the definition of the underyling type of std::string.

Instead of specializing basic_string, you should create your own character datatype in your own namespace, then you should specialize std::char_traits for that type (but nothing else in namespace std), and then you should make a convenient typedef for basic_string instantiated with those types in your own namespace (not in namespace std, which belongs to the C++ standard and shouldn't be altered by users).

Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
  • 1
    It's perfectly legal to specialize existing templates in `std`, so long as all rules are adhered to. – Pavel Minaev Apr 23 '10 at 21:22
  • Making your own char type and your own string has a completely different purpose than specializing std::string (to subtly change it's behavior). – SoapBox Apr 23 '10 at 21:22
  • @SoapBox, that may be so, but modifying a standard container, even if it were permitted, would still be a terrible thing to do and horribly confusing to other developers. – Michael Aaron Safyan Apr 25 '10 at 01:52