Search code examples
c++stringbooststring-comparisonstd

Why is this boost::container::string & std::string comparison not working as intended?


Say you have a std::string and a boost::container::string just like this :

std::string                stdString     =   "This is a test";
boost::container::string   boostString   =   "This is a test";

Say you want to compare their content ; the following is not possible because we cannot compare their types :

stdString == boostString                 // no operator "==" matches these operands

You then choose to use both of their methods .c_str() to get a char* from each string. Not being sure if this does effectivly compare the strings, you try it :

stdString.c_str() == boostString.c_str() // compiles, but comparison returns false

You then try to only use c_str() method from the std::string :

stdString.c_str() == boostString         // compiles, and comparison seems fine

You try the opposite out of curiosity and it works as well :

stdString == boostString.c_str()         // compiles, and comparison seems fine

So the question is, why does these two latter comparisons seem to work fine when the first one did not ?

Bonus question : Is this an unreliable way of comparing the content of these strings ?

Full code sample :

#include <boost/container/string.hpp>
#include <iostream>

int main(int argc, char *argv[])
{
  std::string stdString = "This is a test";
  boost::container::string  boostString;
  for (int i = 0; i < 2; ++i)
  {
    if (i == 0)
    {
      boostString = "This is a test";
      std::cout << "Both strings have the same content." << std::endl << std::endl;
    }
    else
    {
      boostString = "This is z test";
      std::cout << std::endl << std::endl;
      std::cout << "Both strings are different from each other." << std::endl << std::endl;
    }

    std::cout << "stdString.c_str() == boostString.c_str() comparison is : ";

    if (stdString.c_str() == boostString.c_str())
      std::cout << "true" << std::endl;
    else
      std::cout << "false" << std::endl;

    std::cout << "stdString.c_str() == boostString comparison is         : ";

    if (stdString.c_str() == boostString)
      std::cout << "true" << std::endl;
    else
      std::cout << "false" << std::endl;

    std::cout << "stdString == boostString.c_str() comparison is         : ";

    if (stdString == boostString.c_str())
      std::cout << "true" << std::endl;
    else
      std::cout << "false" << std::endl;
  }

  return 0;
}

Output given by the sample :

> Both strings have the same content.
> 
> stdString.c_str() == boostString.c_str() comparison is : false
> stdString.c_str() == boostString comparison is         : true
> stdString == boostString.c_str() comparison is         : true
> 
> 
> Both strings are different from each other.
> 
> stdString.c_str() == boostString.c_str() comparison is : false
> stdString.c_str() == boostString comparison is         : false
> stdString == boostString.c_str() comparison is         : false

Solution

  • With stdString.c_str() == boostString.c_str() you don't compare the strings, you compare the pointers returned by each objects c_str function. And they will most certainly not be equal. If you want to compare strings C-style use std::strcmp.


    The reason e.g. stdString.c_str() == boostString works is because boost::container::string have a non-explicit constructor taking a const char* argument, just what stdString.c_str() is returning. That means stdString.c_str() == boostString is actually equal to boost::container::string(stdString.c_str()) == boostString, which compares two boost::container::string objects.

    Same for stdString == boostString.c_str(), it's equal to stdString == std::string(boostString.c_str()).


    If you need to mix std::string and boost::container::string, and need to use some specific operators with that mix, then you can always overload those operators yourself.

    For example

    bool operator==(std::string const& stdString, boost::container::string const& boostString)
    {
        // If the length of the strings differs, then they're not equal and we return false
        // Otherwise, compare the actual contents of the strings
        return stdString.length() == boostString.length() &&
               std::memcmp(stdString.c_str(), boostString.c_str(), stdString.length()) == 0;
    }
    
    bool operator==(boost::container::string const& boostString, std::string const& stdString)
    {
        return stdString == boostString;  // Calls the previously defined operator== overload
    }
    

    Both are needed so you can use either type on either side of ==.

    Also note that I'm using std::memcmp for the comparison, because either string could contain embedded zeros, which would otherwise act as the string terminator for std::strcmp.