20
// This is a header file.

class MyClass; // It can be forward declared because the function uses reference.
// However, how can I do forward declaraion about std::wstring?
// class std::wstring; doesn't work.
VOID Boo(const MyClass& c);
VOID Foo(const std::wstring& s);
Benjamin
  • 10,085
  • 19
  • 80
  • 130
  • 1
    Because if it *can* be forward declaration it would be better than importing header file. – Benjamin Jan 26 '11 at 04:35
  • 3
    @Fred Nurk: Presumably because it would compile faster. – TonyK Jan 26 '11 at 08:55
  • @TonyK: I was expecting that: but dropping build times down from 253 seconds to 252.8 seconds is not a benefit worth pursuing. I was hoping Benjamin had a much better reason that I can't think of. – Fred Nurk Jan 26 '11 at 09:08
  • 1
    @Fred: I didn't want to include unnecessery header files if I **can**. The header(in question) treats only references. So it needs to know class name. If I include *MyClass.h* and *string* it will makes dependency to other modules. – Benjamin Jan 26 '11 at 09:15
  • @Benjamin: Why is removing a dependency on a benefit to you? The header that needs should be able include it without affecting other headers. – Fred Nurk Jan 26 '11 at 09:17
  • @Fred: file won't change. So there is no benefit I can get. It was qurious. Even if the file never change, I want to do forward declaration if it is possible. – Benjamin Jan 26 '11 at 09:19
  • "So there is no benefit I can get. ...I want to do forward declaration if it is possible." I cannot understand this; if you think there is no benefit, *why* do you want to do it? Have I possibly misunderstood? – Fred Nurk Jan 26 '11 at 09:28
  • If you are compiling with GCC you may be able to use the libstdc++ stringfwd.h header: http://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a01077.html – Ponkadoodle Jul 25 '12 at 19:30
  • 1
    @FredNurk Benjamin seems to think will never change. How can anyone be sure not a single character in that file will **ever** change no matter how many forward language revisions we have in C++? C++2099? That's trivial to predict? It is never good to pull in extra header information before it is needed. (Required perhaps, C++ standard perhaps, but not good.) –  May 05 '14 at 14:56

5 Answers5

36

You can't. #include <string>, you have (almost) no choice.

The reason is that wstring is defined in namespace std and is typedef'd to std::basic_string<wchar_t>. More elaborately, std::wstring is std::basic_string<wchar_t, std::char_traits<wchar_t> >. This means that in order to forward-declare std::wstring you'd have to forward-declare std::char_traits<> and std::basic_string<> inside namespace std. Because (apart from a few exceptions) the standard forbids adding definitions or declarations to namespace std (17.4.3.1/1) ultimately you can't forward-declare any standard template or type in a standard-conforming way. Specifically, this means you can't forward-declare std::wstring.

And yes, we all agree it would be convenient to have a <stringfwd> header, like <iosfwd> for <iostream>. But there isn't. <string> is also not nearly as hardcore to compile as <iostream>, but nevertheless. You have two choices: #include<string> or use an opaque pointer.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • 1
    Implementations are not allowed to add defaulted template parameters to basic_string. But I agree, including is easy enough, with no real drawback. – Fred Nurk Jan 26 '11 at 04:53
  • @Fred: Why not? The standard says what code must compile, and doesn't disallow vendor extensions. So long as the default values left `basic_string` operating exactly as it does in the standard, the compiler vendor is free to add whatever extra template parameters they wish. – Billy ONeal Jan 26 '11 at 04:54
  • Argh. I can't find the reference, but for some reason I remember reading that standard template classes can have extra parameters with defaults. – wilhelmtell Jan 26 '11 at 04:57
  • 1
    @Billy: When basic_string is passed as a template template parameter (`template – Fred Nurk Jan 26 '11 at 05:21
  • 1
    @Wilhelmtell, I'm sure I've read some of the same stuff, but I don't recall where anymore. Wherever it was, it was wrong: http://stackoverflow.com/questions/1469743/standard-library-containers-with-additional-optional-template-parameters – Rob Kennedy Jan 26 '11 at 05:54
  • Ok, the whole thing about default template parameters in standard classes isn't very clear to me. But if it is true that implementors aren't allowed to extend the standard template parameters, then you _can_ forward-declare `std::wstring` and other standard types in a portable manner. – wilhelmtell Jan 26 '11 at 06:15
  • 1
    @wilhelmtell: Indeed, [you can in a standard-conforming way](http://stackoverflow.com/questions/4801411/forward-declaration-of-stdwstring/4802488#4802488); just remember that standard conforming is not always the same as portable. :) (I think it's a safe bet this is portable, however.) – Fred Nurk Jan 26 '11 at 08:21
  • @FredNurk: You can't in a standard conforming way because to do so you would have to add a declaration to the `std` namespace (even if it is a duplicate of another declaration) which violates the constraints on conforming programs 17.4.3.1 [lib.reserved.names] / 1 . – CB Bailey Jan 26 '11 at 08:33
  • I took another look at n1905 and revised the answer. Hopefully the answer is correct this time? – wilhelmtell Jan 26 '11 at 15:45
  • nb : Opaque pointer will not help for arguments (as is the case in the 'question') – Ad N May 07 '13 at 12:02
  • A couple of years ago I set up a simple test, I created a simple file which included either nothing, iostream, string, or vector under GCC 4.6.3 on a dual Xeon E5540 build system. `` was the most expensive of them all, at 260ms (adding 26 wall seconds for every 100 compilation units). I haven't tested it again more recently or with clang. – kfsone Jan 21 '14 at 06:56
  • @FredNurk How do you possibly get "no drawback" out of that answer? wilhelmtell said "you have no choice" and even agreed a forward header would be a wonderful thing (he even named it!)... We just don't have it. –  May 05 '14 at 15:00
  • Interesting, I found a /usr/include/c++/10/bits/stringfwd.h on my computer doing that job, but it seems to be non-standard (like bits/stdc++.h). For instance, Travis CI compiled for Linux but failed on OSX. I could either include that, or , depending on the platform (using preprocessing), but not sure if it's worth it. – hsandt Nov 28 '20 at 16:48
4

I don't think avoiding #include <string> gains you any real benefit, but this is how you could do it:

namespace std {
  template<class Char>
  struct char_traits;

  template<class T>
  struct allocator;

  template<class Char, class Traits, class Allocator>
  struct basic_string;

  typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >
    wstring;
}

// simple test that it's compatible:
std::wstring *p;  // incomplete type at this point, but you can have a pointer
#include <string>
int main() {
  std::wstring s = L"Hello, world!";
  p = &s;
  return 0;
}

You have to be careful of default parameters; in particular, this would not work:

namespace std {
  template<class Char>
  struct char_traits;

  template<class T>
  struct allocator;

  template<class Char, class Traits=char_traits<Char>,
           class Allocator=allocator<Char> >
  struct basic_string;

  typedef basic_string<wchar_t> wstring;
}

#include <string>

Compiled with the include shows the incompatibility:

In file included from /usr/include/c++/4.4/string:41,
                 from example.cpp:15:
/usr/include/c++/4.4/bits/stringfwd.h:52: error: redefinition of default argument for ‘class _Traits’
example.cpp:8: note: original definition appeared here
Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
  • The **reason** to forward declare would be if for some reason `#include ` is 800 lines of code I just don't need (or want, due to bug-tracing) in a simple header file. What if the header is the methods available in a portable DLL and the calling EXE doesn't know or care what a `std::string` is in terms of implementation (as the DLL does all the work)? Using `void*` or an opaque pointer no one has ever seen before does not a strongly typed environment make. Why should I be making a custom wrapper for such a fundamental data type (string)? –  May 05 '14 at 15:04
  • Actually the error you show happens when stringfwd.h is available for your compiler and machine... in which case you could include that instead. But since you don't know if you have it or not (it's not standard), I guess you should go safe (note other answers mentioning that custom definitions in std is not standard; and yes, it's exactly what stringfwd.h does, it's just not in your own code). – hsandt Nov 28 '20 at 16:51
3

std::wstring is a template instantiation, so you can't just forward declare it. You'll have to use the header file.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
2

You can't forward declare std::wstring in a conforming implementation, not because it is a typedef for a template specialization or that there is any possibility that it has an unknown number of template arguments (it doesn't; these are strictly specified) but because there is a constraint on conforming programs that prohibits them from adding any declarations or definitions to the std namespace other than explicit specializations of standard templates which are specialized on a user-defined type.

This constraint is stated in 17.4.3.1 [lib.reserved.names] / 1. There is no exception for forward declarations of std::wstring, you must #include <string> to make a declaration std::wstring available in a conforming way.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 2
    I've always interpreted that as adding a new name, like std::CharlesBailey, rather than declaring one that "exists". (Perhaps more clearly: "add" is the significant word there, rather than saying "It is undefined for a C++ program to declare or define within namespace std...".) But I can't say your interpretation is completely wrong. However, at least given an implementation which does not add template parameters (which is definitely not strictly conforming), it should be portable (and of questionable conformity) to declare as I did in my answer. – Fred Nurk Jan 26 '11 at 08:48
  • @FredNurk: The world would be a dull (if simple) place if everyone interpreted things in the same way! – CB Bailey Jan 26 '11 at 08:54
  • I have checked the [issues list](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-index.html) and don't see anything on this one way or the other. [#120](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#120) looks to be the most relevant, but as an explicit instantiation (which is a definition) isn't required for this declaration, I don't see that it applies. – Fred Nurk Jan 26 '11 at 09:02
-2

class std::wstring; doesn't compile. But this does:

namespace std{
class wstring;
}

However, this declaration is incompatible with the <string> header file, which you should see if you #include <string> after it. So it's really not safe.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • 1
    It's true that this is incompatible, but that simply means you don't do it this way. This doesn't say anything about the "safety" of doing it the right way. – Fred Nurk Jan 26 '11 at 09:05