为什么 c++ string1.size() 减去 string2.size() 得到错误的答案

Why c++ string1.size() minus string2.size() got wrong answer?

本文关键字:size 错误 答案 减去 c++ string1 为什么 string2      更新时间:2023-10-16
#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;
}

输出:为什么是0<-1?按任意键继续...

为什么我们得到这个输出?我认为不应该执行"if"语句,因为 0 大于 3-4=-1。谢谢!

std::string::size的返回类型是 std::size_t

这是一个无符号类型。因此,由于您的减法导致负值,它将基于二的补码进行环绕,导致非常大的数字远远大于0

由于历史原因,标准库通常使用 size_t无符号类型)作为大小函数的结果类型。一个例外是 std::count .但std::string::size()size_t功能之一,

由于该语言保证了无符号类型表达式的模块化算术,因此您可以获得环绕,其中-1成为无符号类型的最大值。


另一个和IMO更明显的愚蠢后果是

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

有保证的。只是纯粹的废话作为源代码文本,当人们回想起高级源代码的目的是更清楚地与人类沟通时。


为了避免这种混乱,你可以定义一个逻辑有符号Size类型作为ptrdiff_t的别名(这是size_t的有符号类型对应物)和大小相同的函数static_sizen_itemslength,加上一个检查器is_empty,如下所示:

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

如果你想直接部分或全部使用此代码,而不从cppx库中引入支持文件,你必须翻译例如 In_到您喜欢的逻辑参数类型,依此类推。cppx 库只是一件非常正在进行的事情,但它已经可以按原样使用。它提供了诸如上述内容的内容,这些基础知识以某种方式错过了成为标准库的一部分,甚至没有被提议。