48

I am learning C++ on my own. I have the following code but it gives error.

#include <iostream>
#include <string>
using namespace std;


int setvalue(const char * value)
{
    string mValue;
    if(value!=0)
    {
       mValue=value;
    }
    else
    {
       mValue=0;
    }
}

int main ()
{
 const char* value = 0;
 setvalue(value);

 cin.get();
 return 0;
}

So want to create a function which accepts char pointers and I want to pass a pointer to it. The function assigns the pointer to its member variable. I'm passing a null pointer intentionally. Following is the error I'm getting:

 D:\CPP\TestCP.cpp In function `int setvalue(const char*)': 

 note C:\Dev-Cpp\include\c++\3.4.2\bits\basic_string.h:422 candidates are: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

 note C:\Dev-Cpp\include\c++\3.4.2\bits\basic_string.h:422                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

 note C:\Dev-Cpp\include\c++\3.4.2\bits\basic_string.h:422                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

it's basically complaining about line: mValue=0;

Why is it complaining about this line? I can't assign a null to a String?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
Maria
  • 605
  • 2
  • 6
  • 8
  • 3
    What are you trying to accomplish by setting it to 0? – James M Jul 23 '12 at 17:38
  • The purpose of the function is to take the char* passed to the function and assign it to it's member string variable. So I'm checking first if it is null, if it is null, then instead of dereferencing a null pointer, I'm assigning null to String. So the purpose is to have mValue and Value to contain similar values. Thanks. – Maria Jul 23 '12 at 17:41
  • 1
    But `mValue = value;` would just do `mValue = 0;` when `value` is a null pointer, if `mValue = 0;` were valid, so the extra check doesn't really do anything. It would be just as invalid. –  Jul 23 '12 at 17:43
  • You might want to look into `boost::optional` if you're set on distinguishing between an empty string and no string. – chris Jul 23 '12 at 17:47
  • @Maria - You cannot learn C++ by trying it out by yourself, you need [a good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). Also gcc 3.4.2 is not a good compiler anymore, it is very old. – Bo Persson Jul 23 '12 at 18:40
  • Possible duplicate of [How to allow a std:string parameter to be NULL?](http://stackoverflow.com/questions/6884300/how-to-allow-a-stdstring-parameter-to-be-null) – krlmlr Apr 04 '16 at 13:39

8 Answers8

84

I can't assign a null to a String?

No. std::string is not a pointer type; it cannot be made "null." It cannot represent the absence of a value, which is what a null pointer is used to represent.

It can be made empty, by assigning an empty string to it (s = "" or s = std::string()) or by clearing it (s.clear()).

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 6
    Note that you get the compile error with `NULL` or `0`. However using `nullptr` compiles since it's not an int and the assignment operator for `char*` is chosen. It will error at runtime though. – Zitrax Feb 16 '17 at 11:24
19

You cannot assign NULL or 0 to a C++ std::string object, because the object is not a pointer. This is one key difference from C-style strings; a C-style string can either be NULL or a valid string, whereas C++ std::strings always store some value.

There is no easy fix to this. If you'd like to reserve a sentinel value (say, the empty string), then you could do something like

const std::string NOT_A_STRING = "";

mValue = NOT_A_STRING;

Alternatively, you could store a pointer to a string so that you can set it to null:

std::string* mValue = NULL;

if (value) {
    mValue = new std::string(value);
}

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • mValue=new std::string(value); gives error: 11 D:\CPP\TestCP.cpp invalid conversion from `std::string*' to `char' 11 D:\CPP\TestCP.cpp initializing argument 1 of `std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits, _Alloc = std::allocator]' – Maria Jul 23 '12 at 17:48
  • 1
    @Maria- Did you change the declaration of `mValue` from `string myValue` to `string* myValue`? – templatetypedef Jul 23 '12 at 17:49
  • no, I don't want it to be a pointer. Can I do this: minValue = (char*)0 ? – Maria Jul 23 '12 at 17:50
  • 4
    @Maria: No you cannot. It is undefined behavior. What would you expect it to mean anyway? You've already been told you can't have a null string. – Benjamin Lindley Jul 23 '12 at 18:03
7

Literal 0 is of type int and you can't assign int to std::string. Use mValue.clear() or assign an empty string mValue="".

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
5

There are two methods to consider which achieve the same effect for handling null pointers to C-style strings.

The ternary operator

void setvalue(const char *value)
{
    std::string mValue = value ? value : "";

}

or the humble if statement

void setvalue(const char *value)
{
    std::string mValue;
    if(value) mValue = value;

}

In both cases, value is only assigned to mValue when value is not a null pointer. In all other cases (i.e. when value is null), mValue will contain an empty string.

The ternary operator method may be useful for providing an alternative default string literal in the absence of a value from value:

std::string mValue = value ? value : "(NULL)";
David Cormack
  • 326
  • 6
  • 9
  • 4
    To be clear, the reason to check for null and conditionally assign like this is because initialising/assigning an `std::string` from a null pointer causes undefined behaviour and thus should be avoided at all costs. – underscore_d Jul 02 '16 at 16:37
  • @underscore_d Awesome comment. You saved my day. :) – hellodear Oct 20 '16 at 08:20
5

I won't argue that it's a good idea (or the semantics of using nullptr with things that aren't pointers), but it's relatively simple to create a class which would provide "nullable" semantics (see nullable_string).

However, this is a much better fit for C++17's std::optional:

#include <string>
#include <iostream>
#include <optional>

// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b)
{
    if (b)
        return "Godzilla";
    else
        return {};
}

int main()
{
    std::cout << "create(false) returned "
              << create(false).value_or("empty") << std::endl;

    // optional-returning factory functions are usable as conditions of while and if
    if (auto str = create(true))
    {
        std::cout << "create(true) returned " << *str << std::endl;
    }
}

std::optional, as shown in the example, is convertible to bool, or you may use the has_value() method, has exceptions for bad access, etc. This provides you with nullable semantics, which seems to be what Maria was trying to accomplish.

And if you don't want to wait around for C++17 compatibility, see this answer about Boost.Optional.

Community
  • 1
  • 1
monkey0506
  • 2,489
  • 1
  • 21
  • 27
0

The else case is unncecessary, when you create a string object it is empty by default.

Naveen
  • 74,600
  • 47
  • 176
  • 233
0

compiler gives error because when assigning mValue=0 compiler find assignment operator=(int ) for compile time binding but it's not present in the string class. if we type cast following statement to char like mValue=(char)0 then its compile successfully because string class contain operator=(char) method.

-1

Many C APIs use a null pointer to indicate "use the default", e.g. mosquittopp. Here is the pattern I am using, based on David Cormack's answer:

    mosqpp::tls_set(
        MqttOptions->CAFile.length() > 0 ? MqttOptions->CAFile.c_str() : NULL,
        MqttOptions->CAPath.length() > 0 ? MqttOptions->CAPath.c_str() : NULL,
        MqttOptions->CertFile.length() > 0 ? MqttOptions->CertFile.c_str() : NULL,
        MqttOptions->KeyFile.length() > 0 ? MqttOptions->KeyFile.c_str() : NULL
    );

It is a little cumbersome, but allows one to keep everything as a std::string up until the API call itself.

Community
  • 1
  • 1
Robert Calhoun
  • 4,823
  • 1
  • 38
  • 34
  • You could very easily add an inlined utility method to accomplish the same. `char const* c_str(std::string const &s) { return s.length() > 0 ? s.c_str() : nullptr; }` Usage: `mosqpp::tls_set(c_str(MqttOptions->CAFile), c_str(MqttOptions->CAPath), c_str(MqttOptions->CertFile), c_str(MqttOptions->KeyFile));` – monkey0506 Oct 07 '17 at 05:13