a string which can be used ala std::string, preserves case, but comparisons are not case sensitive.

#include <string>
#include <iostream>
#include <cctype>

// case insensitive character traits
// inherited copy (preserves case),
// case insensitive comparison, search
struct traits_nocase : std::char_traits<char>
{
  static bool eq( const char& c1, const char& c2 )
  { return toupper(c1) == toupper(c2) ; }
  static bool lt( const char& c1, const char& c2 )
  { return toupper(c1) < toupper(c2) ; }     
  static int compare( const char* s1, const char* s2, size_t N )
  {
    return strncasecmp( s1, s2, N ) ; // posix
    // mirosoft C++ - use _strnicmp instead
  }
  static const char* find( const char* s, size_t N, const char& a )
  {
    for( size_t i=0 ; i<N ; ++i )
      if( toupper(s[i]) == toupper(a) ) return s+i ;
    return 0 ; 
  }
  static bool eq_int_type ( const int_type& c1, const int_type& c2 )
  { return toupper(c1) == toupper(c2) ; }
};

// string preserves case; comparisons are case insensitive
typedef std::basic_string< char, traits_nocase > string_nocase ;

// make string_nocase work like a std::string 
//           with streams using std::char_traits
// std::basic_istream< char, std::char_traits<char> > (std::istream) and
// std::basic_ostream< char, std::char_traits<char> > (std::ostream)
inline std::ostream& operator<< ( std::ostream& stm, const string_nocase& str )  
{ return stm << reinterpret_cast<const std::string&>(str) ; }

inline std::istream& operator>> ( std::istream& stm, string_nocase& str )  
{ 
  std::string s ; stm >> s ;
  if(stm) str.assign(s.begin(),s.end()) ; 
  return stm ; 
}

inline std::istream& getline( std::istream& stm, string_nocase& str )
{ 
  std::string s ; std::getline(stm,s) ;
  if(stm) str.assign(s.begin(),s.end()) ;
  return stm ; 
}
      
// some examples of using string_nocase
#include <fstream>
#include <iterator>
#include <algorithm>
#include <map>
using namespace std;
      
int main()
{
   string_nocase str1 = "http://www.daniweb.com/forums/forum8.html" ;
   string_nocase str2 = "Http://www.DaniWeb.com/Forums/FORUM8.html" ;
   cout << "str1 == str2 ? " << boolalpha << (str1==str2) << '\n' ;
   
   string_nocase sub_str ; 
   cout << "string to find? " ; getline(cin,sub_str) ;
   if( str1.find( sub_str ) != string_nocase::npos ) 
     cout << "found " << sub_str << " in " << str1 << '\n' ;
   
   ifstream file(__FILE__) ;
   istream_iterator<string_nocase> begin(file), end ; 
   cout << "#includes: " << count( begin, end, "#INCLUDE" ) << '\n' ;
   
   map<string_nocase,int> phone_book ;
   phone_book[ "SALEM" ] = -72 ;
   phone_book[ "~s.o.s~" ] = 1234567 ;
   phone_book[ "salem" ] = 9999999 ;
   cout << phone_book[ "~S.O.S~" ] << '\t' << phone_book[ "SALEM" ] << '\n' ;
}

// what we have done is customize the character traits policy
// std::basic_string is also policy free wrt memory management
// see http://www.ddj.com/dept/cpp/184403784 for some examples of  
// customizing the memory allocator policy.


/**
Output:

> g++ -Wall -std=c++98 string_nocase.cpp && ./a.out
str1 == str2 ? true
string to find? OM/FOR
found OM/FOR in http://www.daniweb.com/forums/forum8.html
#includes: 7
1234567 9999999
*/

Nice snippet, though it would be better if you included the output so that beginners don't have to compile and execute your code to understand what is going on.