Reputation: 343
i could not find threads giving a clear answer on this -
i have a constructor, for example:
FanBookPost::FanBookPost(Fan* owner, std::string content);
Fan is another class in my code, but content is the problematic one:
since std::string is not a pointer, i would expect that calling the constructor with (fan, nullptr) would not be possible. however, it compiles! ... and crashes at runtime with EXC_BAD_ACCESS.
is this avoidable?
Upvotes: 1
Views: 1337
Reputation: 490048
The problem is that std::string
has a non-explicit ctor that takes a char *
as its sole (required) parameter. This gives an implicit conversion from nullptr
to std::string
, but gives undefined behavior, because that ctor specifically requires a non-null pointer.
There are a few ways to prevent this. Probably the most effective would be to take a (non-const) reference to a std::string
, which will require passing a (non-temporary) string
as the parameter.
FanBookPost::FanBookPost(Fan* owner, std::string &content);
This does have to unfortunate side effect of giving the function the ability to modify the string that's passed. It also means that (with a conforming compiler1) you won't be able to pass nullptr
or a string literal to the function--you'll have to pass an actual instance of std::string
.
If you want to be able to pass a string literal, you can then add an overload that takes a char const *
parameter, and possibly one that takes a nullptr_t
parameter as well. The former would check for a non-null pointer before creating a string and calling the function that takes a reference to a string, and the latter would do something like log the error and unconditionally kill the program (or, just possibly, log the error and throw an exception).
That's annoying and inconvenient, but may be superior to the current situation.
Upvotes: 1
Reputation: 49976
How about using a proxy / wrapper typ, if you really want to be safe:
template<typename T>
struct e_t
{
public:
inline e_t ( e_t const & other )
: m_value( other.m_value )
{}
inline T & value( void ) { return m_value; }
inline operator T&() { return m_value; }
inline e_t( const T& c ) : m_value( c ) {}
private:
T m_value;
};
void FanBookPost(int* owner, e_t<std::string> content) {
}
int main()
{
int n = 0;
//FanBookPost(&n, 0); // compiler error
//FanBookPost(&n, nullptr); // compiler error
//FanBookPost(&n, ""); // unfortunately compiler error too
FanBookPost(&n, std::string(""));
}
Upvotes: 1
Reputation: 96233
What you're observing is that you can implicitly create a std::string
from a character pointer (nullptr
in this case) which is then passed to the function. However creating a string
from a null pointer is not allowed. There is nothing wrong with your method signature, just the client use that violates the std::string
constructor's contract.
Upvotes: 3
Reputation: 11482
The problem here is that the crash will occure when the constructor of std::string
is called (as a nullptr interpreted as const char*
is accessed there). There is nothing here you can do against this but to tell other peoples not to do shit. Its not a problem of your constructor and thus not your responsibility (besides that you cant prevent it).
Upvotes: 3