tags:

views:

207

answers:

6

Please refer to the code below. In this code i am storing the const char* returned by test.c_str() into a reference. My question is Will the data be correctly refering to the contents of test. I am thinking that ptr returned by test.c_str() will be a temporary and if i bound it to a reference that reference will not be valid. Is my thinking correct

class RefPtrTest
{
    std::string test;
    StoringClass storingClass;
public: 
    RefPtrTest(): test("hello"), storingClass(test.c_str())
    {
    }
}

where StoringClass is

class StoringClass 
{
    const char*& data;
public: 
    StoringClass (const char*& input): data(input)
    {
    }
}

EDIT1: Lets just not consider what std::string is doing. Suppose i am using my own class called mystring

class RefPtrTest
{
    const mystring test;
    StoringClass storingClass;
public: 
    RefPtrTest(): test("hello"), storingClass(test.getInternalPointer())
    {
    }
}

getInternalPointer directly returns the internal pointer. I want to verify this hypothesis, while storingClass(test.getInternalPointer()) ptr returned by test.getInternalPointer() will be a temporary and if i bound it to a reference that reference will not be valid. Is my thinking correct

EDIT2: That StoringClass is not under my control. Basically its a template clasee where it stores reference to the type. I am using it for const char*. I am aware of all the design issues which you have raised. But i cant change that class, and i have to use it for const char *. there is no other way aroud it.

+3  A: 

Yes, your thinking is correct (test.c_str() returns a temp, so you can't use it to initialize a reference), unless test.c_str() actually returns a reference to a pointer, which I don't think it does... does it?

This should give you a compile error, though, did you try it?

In this particular case, actually using the pointer for something would not make much sense. But if you're only asking about references to pointers, then you're correct (regardless of type).

roe
test.c_str() returns a pointer, test is a `std::string`
Yogesh Arora
I was just unsure of whether std::string::c_str() returns a reference to a pointer.
roe
@roe `const` references to temporaries are legal, see the following: http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry (although using a reference is pointless in this particular case).
Andreas Brinck
@Andreas: this isn't a const reference, it's a non-const reference to a `const char*`. roe is right, this shouldn't compile.
Steve Jessop
@Andreas: from that link you can read that it also only applies to stack based references
roe
Andreas Brinck
@Andreas: ah, I see what you mean now. roe's statement "you can't use it to initialize a reference" is incorrect, because you *could* use it to initialize a const reference, it's just the example happens not to use one. roe should have said, "you can't use it to initialize *that* reference", or some such.
Steve Jessop
+3  A: 

The standard has the following to say about c_str (21.3.6/2)

Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this.

So the answer is no, you can not treat the pointer as a reference to the contents of the string (after any non const function calls on the string).

Andreas Brinck
Yogesh Arora
Andreas Brinck
@Yogesh: If all you want to do is valid, what do you need the reference for in the first place?
UncleBens
The stuff about `c_str` is true but irrelevant. Even if `c_str` returned a pointer which was valid forever (if test was a `const std::string`, for instance), this code would still be broken because of the extra level of indirection. The reference stored in `data` isn't to the string, it's to the return value of `c_str`, which is a temporary and hence ceases to exist as soon as the ctor of `storingClass` returns.
Steve Jessop
@Yogesh: I think you need to look more closely at why `StoringClass` stores a reference to a pointer. Normally you'd only store a non-const reference to something if you want to modify the something. Why does StoringClass need to be able to modify a *pointer*, let alone a pointer which points to some part of a string?
Steve Jessop
@Steve I don't know if your first comment was directed at me, but as you see in my first comment I told Yogesh that he *shouldn't* use a reference. In the OP's case it would be OK to keep a *copy* of the return value of `c_str` in `data`, it will be valid for the lifetime of `test` and as long as no non const member functions are called on `test`.
Andreas Brinck
Yes, my first comment was directed at you. Whether the questioner should be using a reference or not, in point of fact he is using one, and he is asking about the validity *of that reference*. You are telling him about the validity of the value contained in the referand (which is itself a pointer and therefore the concept of validity applies, but that's not what he asked). In fact the validity of the reference is even shorter than the validity of the pointer.
Steve Jessop
So I think the answer to "is my thinking correct" is "yes". He's right to say that `test.c_str()` is a temporary, and therefore storing a reference to it is no good. He has reasoned correctly about this crazy-ass situation, if you see what I mean, and his conclusion is true irrespective of whether or not any non-const calls are made on the string.
Steve Jessop
@Steve Ok, I misinterpreted the question, I guess I was focusing to much on the "My question is Will the `data` be correctly refering to the contents of `test`" bit.
Andreas Brinck
Maybe there should be a rule, only one question in the question body. Especially if the straight answer to one of the questions is "yes", and to the other is "no" :-)
Steve Jessop
A: 

It may be valid. You should not depend on this behaviour, however.

The string class implementation has a char* underneath and c_str() method likely just returns the pointer to the beginning of that array. However, as the string changes over time, this internal array may be moved in the memory, resized, whatsoever. You should invoke c_str() every time you'd like to convert string to char*.

It likely just returns the pointer to internal memory, so is very fast anyway.

pajton
I fail to see the relevance here, he's talking about a reference to a pointer, isn't he? Not whether that pointer makes sense.
roe
@roe 'My question is Will the data be correctly refering to the contents of test'
pajton
+1  A: 

You should not modify the string buffer directly if that's what you're trying to do.

When you're dealing with pointers in general, you only need a reference to a pointer if you want to change the address the pointer type holds. If you only want to change the buffer then you can simply store a char*.

If you want to access a buffer like this use std::vector instead with something like this:

std::vector<char> v;
v.resize(size);
strcpy(&v.front(), "testing");
Brian R. Bondy
+1  A: 

See the following comment about the std::string c_str() member function from cplusplus.com:

The returned array points to an internal location with the required storage space for this sequence of characters plus its terminating null-character, but the values in this array should not be modified in the program and are only granted to remain unchanged until the next call to a non-constant member function of the string object.

Although you don't call any non-const member functions in the code that you posted, this is not a very good idea. When you modify the private std::sting test variable in the RefPtrTest class, you inadvertently affect the StoringClass class (this violates encapsulation of RefPtrTest). Somebody maintaining your code later (maybe you) may not realize this and introduce a bug/crash.

Yukiko
+1  A: 

If StoringClass needs a reference to a pointer, then you have to ensure that there is a pointer for it to refer to (and modify, since it's not a const reference), with a lifetime as long as that of the reference.:

#include <string>
#include <iostream>

template <typename T>
class StoringClass {
    T& data;
public: 
    StoringClass (T& input): data(input) { }
    void print() const { std::cout << data << "\n"; }
    void set(T x) { data = x; }
};

class RefPtrTest {
    const std::string test;
    const char *ptr;
    StoringClass<const char*> storingClass;
public: 
    RefPtrTest(): test("hello"), ptr(test.c_str()), storingClass(ptr) { }
    void print() const { storingClass.print(); }
    void set(const char* x) { storingClass.set(x); }
};

int main() {
    RefPtrTest t;
    t.print();
    t.set("world");
    t.print();
}

Output:

hello
world

IMO StoringClass is a bit weird. For instance, I could have marked the set functions const too, and it'd still work. But if you have to use it, you have to use it. If you can use a StoringClass<const char *const> instead, that might be better: it will ensure that you don't call any functions of StoringClass which modify the pointer. The way you asked the question suggests that you don't expect that to happen.

Edit:

If test wasn't const, though, RefPtrTest could have a function:

void set_another_way(const char *x) { test = x; set(test.c_str()); }

Which would modify the string and update the pointer to which storingClass refers. The function body is equivalent to test = x; ptr = test.c_str();. Assuming single-threaded code, that also addresses any concerns about the validity of the pointer value returned by test.c_str(), provided that set_another_way is the only means by which test is ever modified.

Frankly, StoringClass shouldn't be called StoringClass. It doesn't store anything ;-p

Steve Jessop
I had exactly done the same thing(taking another `const char*`) to fix this problem since this gave me RAII semantics.
Yogesh Arora