3
#include <iostream>
#include <sstream>

template <typename T>
const char* numberToString(T number) {
    std::ostringstream ss;
    ss << number;
    return ss.c_str();
}

int main() {
    printf(numberToString(123));
    return 0;
}

My error:

1>d:\programming\euler\problem 4\problem 4\problem 4\source.cpp(8): error C2039: 'c_str' : is not a member of 'std::basic_ostringstream<char,std::char_traits<char>,std::allocator<char>>'
1>          d:\programming\euler\problem 4\problem 4\problem 4\source.cpp(26) : see reference to function template instantiation 'const char *numberToString<int>(T)' being compiled
1>          with
1>          [
1>              T=int
1>          ]

Why doesn't this work?

Tetramputechture
  • 2,911
  • 2
  • 33
  • 48
  • 1
    Documentation: http://en.cppreference.com/w/cpp/io/basic_ostringstream – vz_ Dec 20 '13 at 20:57
  • 2
    You cant get the `const char*` ( c_str() ) from a temporary string. Once you return from the numberToString your `stringstream` will be destroyed and its underlying `std::string` will be destroyed also. When you invoke `c_str()` it will return a pointer to something that is valid only through the `std::string` life. I suggest working with `std::string`, since it is a object (not a pointer) it will be copied when needed. – André Puel Dec 20 '13 at 20:59
  • 4
    FYI, the standard library offers this functionality, there's no need to write a function for it. http://en.cppreference.com/w/cpp/string/basic_string/to_string – Praetorian Dec 20 '13 at 21:01
  • You read the documentation, hein? ... –  Dec 20 '13 at 21:07

4 Answers4

7

c_str is a member of std::string, not ostringstream. If you want to get a string out of the stream, use str(). Note, however, that returning a const char* from that string is wrong - the string will go out of scope before you can use the const char*. Therefore, have your function return a string (or have it get a buffer to write to):

template <typename T>
std::string numberToString(T number) {
    std::ostringstream ss;
    ss << number;
    return ss.str();
}
Eran
  • 21,632
  • 6
  • 56
  • 89
4

c_str() does not exist for std::ostringstream. What you meant was:

template <typename T>
const char* numberToString(T number) 
{
    std::ostringstream ss;
    ss << number;
    return ss.str().c_str();
}

After you make that change, you will have another problem: you will be returning a pointer to a buffer that was just destroyed. To fix that, you should return a std::string:

template <typename T>
std::string numberToString(T number) 
{
    std::ostringstream ss;
    ss << number;
    return ss.str();
}

Which you can do with std::to_string already, so it is really pointless to write your own function.

Zac Howland
  • 15,777
  • 1
  • 26
  • 42
  • It isn't pointless if you don't have a C++11 compiler, and many people do not. However, it is a nice recommendation for people who do have a c++11 compiler. – shawn1874 Dec 20 '13 at 21:20
  • @shawn1874 Fair enough ... though, at this point, all the old compilers should be phasing out quickly. Hell, C++14 is in its final stages of being approved. – Zac Howland Dec 20 '13 at 21:21
2

That's because c_str() is the member function of std::string which returns a const char*.

To get the underlying string of a strinstream, you must use str().

The error was pretty much self explanatory:

error C2039: 'c_str' : is not a member of 'std::basic_ostringstream

Note however that you're returning a pointer to something (the underlying data of the temporary string returned by str()) that will not exist after the return statement (i.e. in the calling code) and that manipulating that pointer will quite sureley end up in undefined behavior.

As you are in C++, you could rather return the std::string directly, and output it with

std::cout << numberToString(123);

which would be safer.

JBL
  • 12,588
  • 4
  • 53
  • 84
  • It won't be a pointer to the stream's internal data, `str()` returns a copy of the string contained within the stream. Regardless, it will still be a pointer to a temporary that will be destroyed at the end of that expression. – Praetorian Dec 20 '13 at 21:04
  • @Praetorian Right! Corrected. – JBL Dec 20 '13 at 21:06
2

You want to do:

template <typename T>
std::string numberToString(T number) {
    std::ostringstream ss;
    ss << number;
    return ss.str();
}

int main() {
    std::cout << numberToString(123);
    return 0;
}

To get the underlying std::string in std::ostringstream, and then the resulting c-style string in std::string. As others have pointed out, the pointer returned by c_str goes out of scope, and therefore you must copy it to a buffer or to another std::string. If you insist on using printf, then use c_str on the function call:

printf("%s", numberToString(123).c_str());

For more information, see Is it a good idea to return " const char * " from a function?

Depending on what somestlstring is and what is being done there.

If it is a local variable you are returning a pointer into memory that is being released when GetSomeString completes, so it is a dangling pointer and an error.

It all boils down to the lifetime of somestlstring and the operations you perform on it. The pointer returned by .c_str() is guaranteed to be valid only up to the next mutating operation in the string. So if something changes somestlstring from the call to .c_str() and before s is constructed you will be in undefined behavior land.

However, you can simply use std::to_string.

std::string s = std::to_string(123);
Community
  • 1
  • 1
  • I did that, but now my program isn't outputting anything. What's wrong? – Tetramputechture Dec 20 '13 at 21:01
  • Why do you need that `temp` for? Just go ahead and `return ss.str()`. Or at least do `temp = ss.str()`. Your wasting time for nothing creating a `string`, getting its internal buffer and constructing yet another `string`. – Eran Dec 20 '13 at 21:08