Document BasicStringWriter
diff --git a/doc/api.rst b/doc/api.rst
index 4ed9ae8..2cde812 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -157,6 +157,9 @@
 .. doxygenclass:: fmt::BasicArrayWriter
    :members:
 
+.. doxygenclass:: fmt::BasicStringWriter
+   :members:
+
 .. doxygenfunction:: bin(int)
 
 .. doxygenfunction:: oct(int)
diff --git a/fmt/string.h b/fmt/string.h
index 4fc1f76..1f82f95 100644
--- a/fmt/string.h
+++ b/fmt/string.h
@@ -14,6 +14,89 @@
 
 namespace fmt {
 
+namespace internal {
+
+// A buffer that stores data in ``std::string``.
+template <typename Char>
+class StringBuffer : public Buffer<Char> {
+ private:
+  std::string data_;
+
+ protected:
+  virtual void grow(std::size_t size) {
+    data_.resize(size);
+    this->ptr_ = &data_[0];
+    this->capacity_ = size;
+  }
+
+ public:
+  // Moves the data to ``str`` clearing the buffer.
+  void move_to(std::string &str) {
+    data_.resize(this->size_);
+    str.swap(data_);
+    this->capacity_ = this->size_ = 0;
+    this->ptr_ = 0;
+  }
+};
+}  // namespace internal
+
+/**
+  \rst
+  This class template provides operations for formatting and writing data
+  into a character stream. The output is stored in ``std::string`` that grows
+  dynamically.
+
+  You can use one of the following typedefs for common character types
+  and the standard allocator:
+
+  +---------------+----------------------------+
+  | Type          | Definition                 |
+  +===============+============================+
+  | StringWriter  | BasicStringWriter<char>    |
+  +---------------+----------------------------+
+  | WStringWriter | BasicStringWriter<wchar_t> |
+  +---------------+----------------------------+
+
+  **Example**::
+
+     StringWriter out;
+     out << "The answer is " << 42 << "\n";
+
+  This will write the following output to the ``out`` object:
+
+  .. code-block:: none
+
+     The answer is 42
+
+  The output can be moved to an ``std::string`` with ``out.move_to()``.
+  \endrst
+ */
+template <typename Char>
+class BasicStringWriter : public BasicWriter<Char> {
+ private:
+  internal::StringBuffer<Char> buffer_;
+
+ public:
+  /**
+    \rst
+    Constructs a :class:`fmt::BasicStringWriter` object.
+    \endrst
+   */
+  BasicStringWriter() : Writer(buffer_) {}
+
+  /**
+    \rst
+    Moves the buffer content to *str* clearing the buffer.
+    \endrst
+   */
+  void move_to(std::string &str) {
+    buffer_.move_to(str);
+  }
+};
+
+typedef BasicStringWriter<char> StringWriter;
+typedef BasicStringWriter<wchar_t> WStringWriter;
+
 /**
   \rst
   Converts *value* to ``std::string`` using the default format for type *T*.
diff --git a/test/string-test.cc b/test/string-test.cc
index 5ef8877..06265da 100644
--- a/test/string-test.cc
+++ b/test/string-test.cc
@@ -10,6 +10,63 @@
 #include "fmt/string.h"
 #include "gtest/gtest.h"
 
+using fmt::internal::StringBuffer;
+
+TEST(StringBufferTest, Empty) {
+  StringBuffer<char> buffer;
+  EXPECT_EQ(0, buffer.size());
+  EXPECT_EQ(0, buffer.capacity());
+  std::string data;
+  // std::string may have initial capacity.
+  std::size_t capacity = data.capacity();
+  buffer.move_to(data);
+  EXPECT_EQ("", data);
+  EXPECT_EQ(capacity, data.capacity());
+}
+
+TEST(StringBufferTest, Reserve) {
+  StringBuffer<char> buffer;
+  std::size_t capacity = std::string().capacity() + 10;
+  buffer.reserve(capacity);
+  EXPECT_EQ(0, buffer.size());
+  EXPECT_EQ(capacity, buffer.capacity());
+  std::string data;
+  buffer.move_to(data);
+  EXPECT_EQ("", data);
+}
+
+TEST(StringBufferTest, Resize) {
+  StringBuffer<char> buffer;
+  std::size_t size = std::string().capacity() + 10;
+  buffer.resize(size);
+  EXPECT_EQ(size, buffer.size());
+  EXPECT_EQ(size, buffer.capacity());
+  std::string data;
+  buffer.move_to(data);
+  EXPECT_EQ(size, data.size());
+}
+
+TEST(StringBufferTest, MoveTo) {
+  StringBuffer<char> buffer;
+  std::size_t size = std::string().capacity() + 10;
+  buffer.resize(size);
+  const char *p = &buffer[0];
+  std::string data;
+  buffer.move_to(data);
+  EXPECT_EQ(p, &data[0]);
+  EXPECT_EQ(0, buffer.size());
+  EXPECT_EQ(0, buffer.capacity());
+}
+
+TEST(StringWriterTest, MoveTo) {
+  fmt::StringWriter out;
+  out << "The answer is " << 42 << "\n";
+  std::string s;
+  out.move_to(s);
+  EXPECT_EQ("The answer is 42\n", s);
+  EXPECT_EQ(0, out.size());
+}
+
 TEST(StringTest, ToString) {
   EXPECT_EQ("42", fmt::to_string(42));
 }