Zephyer
Zephyer

Reputation: 343

C++ How to detect when nullptr is passed to function where std::string is expected?

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

Answers (4)

Jerry Coffin
Jerry Coffin

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.


  1. Unfortunately, the last time I noticed MS VC++ did not conform in this respect. It allows passing a temporary object by non-const reference. Normally that's fairly harmless (it just lets you modify the temporary, but that normally has no visible side effects). In this case it's much more troublesome though, since you're depending on it specifically to prevent passing a temporary object.

Upvotes: 1

marcinj
marcinj

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

Mark B
Mark B

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

Sebastian Hoffmann
Sebastian Hoffmann

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

Related Questions