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

int main()
{
  string haystack = "aaa";
  string needle = "aaaa";
  int i = 0;
  if(i <= haystack.size() - needle.size())
    cout<<"why 0<-1?"<<endl;
  return 0;
}

Output: why 0<-1? Press any key continue...

Why we got this output? I think "if" statement should not be executed cause 0 is greater than 3-4=-1. Thanks!

jianhui
  • 133
  • 6

2 Answers2

10

The return type of std::string::size is std::size_t.

This is an unsigned type. Therefore since your subtraction results in a negative value, it will wrap around based on two's complement, resulting in a very large number far greater than 0.

lisyarus
  • 15,025
  • 3
  • 43
  • 68
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
2

For historical reasons the standard library generally uses size_t, which is an unsigned type, as the result type of size functions. One exception is std::count. But std::string::size() is among the size_t functions,

Since the language guarantees modular arithmetic for unsigned type expressions, you get wrap-around where -1 becomes the largest value of the unsigned type.


Another and IMO more obviously silly consequence is that

std::string( "Hello" ).size() < -1

is guaranteed. Just pure nonsense as source code text, when one recalls that the purpose of high level source code is to communicate more clearly to humans.


To avoid this mess you can define a logical signed Size type as an alias for ptrdiff_t (which is the signed type counterpart to size_t) and ditto size functions static_size, n_items and length, plus a checker is_empty, as follows:

sizes_and_lengths.hpp
#pragma once
// p/cppx/core_language_support/sizes_and_lengths.hpp
// Copyright © Alf P. Steinbach 2015. Boost Software License 1.0.

#include <p/cppx/core_language_support/basic_types.hpp>
#include <p/cppx/core_language_support/overloading.hpp>             // cppx::(Object_argument etc.)
#include <p/cppx/core_language_support/type_builders.hpp>           // cppx::(Ptr_, Ref_, Array_of_, ...)
#include <p/cppx/core_language_support/tmp/If_.hpp>                 // cppx::(If_, Is_)
#include <p/cppx/core_language_support/tmp/type_relationships.hpp>  // cppx::Is_convertible_to_

#include <string>           // std::basic_string
#include <utility>          // std::declval

namespace progrock{ namespace cppx{
    // These templates use size_t to accommodate g++, which has bug in this area.

    // static_size()
    // Static capacity of a collection.

    template< class Type, size_t n >
    constexpr
    auto static_size( In_ref_<Raw_array_of_<n, Type>> )
        -> Size
    { return n; }

    template< class Type, size_t n >
    constexpr
    auto static_size( In_ref_<Array_of_<n, Type>> )
        -> Size
    { return n; }

    template< size_t n >
    constexpr
    auto static_size( In_ref_<std::bitset<n>> )
        -> Size
    { return n; }


    // n_items() & is_empty()
    // Right-typed (signed integer) dynamic size of a collection.

    template< class Type >
    auto n_items( In_ref_<Type> o )
        -> Size
    { return o.size(); }

    template< size_t n >
    auto n_items( In_ref_<std::bitset<n>> o )
        -> Size
    { return o.count(); }       // Corresponds to std::set<int>::size()

    namespace impl {
        using std::declval;

        template< class Type >
        constexpr
        auto has_std_empty_checker( ... )
            -> bool
        { return false; }

        template< class Type >
        constexpr
        auto has_std_empty_checker( int )
            -> If_<
                Is_<bool, decltype( declval<const Type>().empty() )>,   // CFINAE
                Then_<bool>                                             // SFINAE
                >
        { return true; }

        struct Using_empty_method
        {
            template< class Type >
            auto is_empty( In_ref_<Type> o ) const
                -> bool
            { return o.empty(); }
        };

        struct Using_n_items_function
        {
            template< class Type >
            auto is_empty( In_ref_<Type> o ) const
                -> bool
            { return (n_items( o ) == 0); }
        };

    }  // namespace impl

    template< class Type >
    constexpr
    auto has_std_empty_checker()
        -> bool
    { return impl::has_std_empty_checker<Type>( 42 ); }

    template< class Type >
    auto is_empty( In_ref_<Type> o )
        -> bool
    {
        using Impl = Ifc_<
            has_std_empty_checker<Type>(),
            Then_<impl::Using_empty_method>,
            Else_<impl::Using_n_items_function>
            >;
        return Impl().is_empty( o );
    }

    template< size_t n >
    auto is_empty( In_ref_<std::bitset<n>> bits )
        -> bool
    { return bits.none(); }


    // length() & length_of_literal() & is_empty
    // Lengths of strings.

    template< class Char, size_t n >
    constexpr
    auto length_of_literal( In_ref_<Raw_array_of_<n, Char>> )   // "length" wraps this.
        -> Size
    { return n - 1; }

    template< class Type >
    auto length( Object_argument, In_ref_<Type> s )
        -> Size
    { return s.length(); }

    template< class Char
        , class Enabled_ = If_< Is_in_< Character_types, Char > >
        >
    auto length( Pointer_argument, In_value_<Ptr_<const Char>> s )
        -> Size
    {
        auto p = s;
        while( *p != 0 ) { ++p; }
        return p - s;
    }

    template< class Char, size_t n
        , class Enabled_ = If_< Is_in_< Character_types, Char > >
        >
    auto constexpr length( Array_argument, In_ref_<Raw_array_of_<n, Char>> a )
        -> Size
    { return length_of_literal( a ); }

    template< class Type >
    auto length( In_ref_<Type> o )
        -> Size
    { return length( Arg_kind_<Type>(), o ); }

    template< class Char
        , class Enabled_ = If_< Is_in_< Character_types, Char > >
        >
    auto is_empty( In_value_<Ptr_<const Char>> s )
        -> bool
    { return (*s == 0); }

}}  // namespace progrock::cppx

If you want to use this code, in part or in whole, directly without bringing in the supporting files from the cppx library, you'll have to translate e.g. In_ to the type you prefer for a logical in-argument, and so on. The cppx library is just a thing very much in-progress, but it's usable as-is already. It provides stuff such as the above, the very basics that somehow missed becoming part of the standard library, or even being proposed.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331