【问题标题】:Portability and length of std::string on Windows vs. LinuxWindows 与 Linux 上 std::string 的可移植性和长度
【发布时间】:2019-04-23 01:19:51
【问题描述】:

我遇到了一些 C++11 std::string 长度的可移植性问题。在 Windows 上是 long long unsigned int,但在 Linux 和 Mac 上是 long unsigned int。我的理解是,使用auto 是解决此类问题的标准方法,但我很难找到一种可移植的方式来通过类接口公开这些属性。


以下类在 Linux GCC 7.3.0(以及 MacOS)上编译和运行没有问题:

g++ -g -O2 -std=c++11 -Werror=conversion stringwrap.cc
./a.out
3

但在 Windows (g++ 8.1.0 MinGW-W64 x86_64-posix-seh-rev0) 上,我得到以下编译错误:

C:\temp\v0.11>g++ -g -O2 -std=c++11 -Werror=conversion stringwrap.cc
In file included from stringwrap.cc:1:
stringwrap.h: In function 'long unsigned int determineFirstPosition(std::__cxx11::string)':
stringwrap.h:35:20: error: conversion from 'std::__cxx11::basic_string<char>::size_type' {aka 'long long unsigned int'}
to 'long unsigned int' may change value [-Werror=conversion]
     return s.length();
            ~~~~~~~~^~
stringwrap.cc: In member function 'long unsigned int Stringwrap::length() const':
stringwrap.cc:9:23: error: conversion from 'std::__cxx11::basic_string<char>::size_type' {aka 'long long unsigned int'}
to 'long unsigned int' may change value [-Werror=conversion]
     return str_.length();
            ~~~~~~~~~~~^~
cc1plus.exe: some warnings being treated as errors

stringwrap.h

#include <iostream>
#include <string>

class Stringwrap
{
  private:
    std::string str_;

  public:

    Stringwrap(const std::string& str);

    unsigned long int length() const;

    unsigned long int getFirstPosition() const;
};

inline unsigned long int determineFirstPosition(const std::string s)
{
    for (unsigned long int i = 0; i < s.length(); ++i)
    {
        switch (s.at(i))
        {
            case ' ':
            {
                break;
            }
            default:
            {
                return i;
            }
        }
    }

    return s.length();
}

stringwrap.cc

#include "stringwrap.h"

Stringwrap::Stringwrap(const std::string& str) : str_(str)
{
}

unsigned long int Stringwrap::length() const
{
    return str_.length();
}

unsigned long int Stringwrap::getFirstPosition() const
{
    return determineFirstPosition(str_);
}

int main()
{
    Stringwrap sw = *new Stringwrap("   x   ");
    std::cout << sw.getFirstPosition() << std::endl;
}

我尝试将所有unsigned long ints 更改为auto,而-std=c++11 出现以下错误:

C:\temp\v0.11>g++ -g -O2 -std=c++11 -Werror=conversion stringwrap.cc
In file included from stringwrap.cc:1:
stringwrap.h:13:19: error: 'length' function uses 'auto' type specifier without trailing return type
     auto length() const;
                   ^~~~~
stringwrap.h:13:19: note: deduced return type only available with -std=c++14 or -std=gnu++14
stringwrap.h:15:29: error: 'getFirstPosition' function uses 'auto' type specifier without trailing return type
     auto getFirstPosition() const;
                             ^~~~~
stringwrap.h:15:29: note: deduced return type only available with -std=c++14 or -std=gnu++14
stringwrap.h:18:55: error: 'determineFirstPosition' function uses 'auto' type specifier without trailing return type
 inline auto determineFirstPosition(const std::string s)
                                                       ^
stringwrap.h:18:55: note: deduced return type only available with -std=c++14 or -std=gnu++14
stringwrap.h: In function 'auto determineFirstPosition(std::__cxx11::string)':
stringwrap.h:35:21: error: inconsistent deduction for auto return type: 'int' and then 'long long unsigned int'
     return s.length();
                     ^
stringwrap.cc: At global scope:
stringwrap.cc:7:27: error: 'length' function uses 'auto' type specifier without trailing return type
 auto Stringwrap::length() const
                           ^~~~~
stringwrap.cc:7:27: note: deduced return type only available with -std=c++14 or -std=gnu++14
stringwrap.cc:12:37: error: 'getFirstPosition' function uses 'auto' type specifier without trailing return type
 auto Stringwrap::getFirstPosition() const
                                     ^~~~~
stringwrap.cc:12:37: note: deduced return type only available with -std=c++14 or -std=gnu++14

当我使用auto 并编译--std=c++14 时,出现以下错误:

C:\temp\v0.11>g++ -g -O2 -std=c++14 -Werror=conversion stringwrap.cc
In file included from stringwrap.cc:1:
stringwrap.h: In function 'auto determineFirstPosition(std::__cxx11::string)':
stringwrap.h:35:21: error: inconsistent deduction for auto return type: 'int' and then 'long long unsigned int'
     return s.length();
                     ^

问题:如何编写可移植的 C++11 代码(Linux、Windows),避免在 STL 数据类型(如 std::string)中进行类型转换(如上所示)?

【问题讨论】:

  • C++11不支持返回类型推导,需要使用尾随返回类型auto foo() -&gt; std::string::size_type {...}
  • 只需使用size_t。所描述的问题是使用 32 或 64 位构建类型(系统)的结果。
  • @MarekR 这不是便携式的。没有要求std::string::size_typesize_t
  • @NathanOliver 在某种意义上是。由于 std::string size_type 是std::allocator_traits&lt;Allocator&gt;::size_type,而对于标准分配器size_type 将是make_unsigned_t&lt;difference_type&gt;,而后者是ptrdiff_t,对于所有意图和目的而言,它都变为size_t
  • @NathanOliver 没有要求也许可以,但是size_t应该不小于容器大小类型,因此可以安全使用。

标签: c++ c++11 stl portability stdstring


【解决方案1】:

查看std::string文档,可以看到类型被调用

std::string::size_type

所以就使用它。你不需要知道或猜测它是什么原始类型 of - 你已经有了一个保证正确的可用名称。

【讨论】:

    【解决方案2】:

    std::string 提供公共类型,如std::string::size_type,您可以使用它们来定义您的函数。您可以定义 determineFirstPosition 函数,如

    inline std::string::size_type determineFirstPosition(const std::string s)
    {
        for (std::string::size_type i = 0; i < s.length(); ++i)
        {
            switch (s.at(i))
            {
                case ' ':
                {
                    break;
                }
                default:
                {
                    return i;
                }
            }
        }
    
        return s.length();
    }
    

    如果你不想到处重复std::string::size_type,你可以在你的类中添加一个 using 声明来缩短名字

    using pos_type = std::string::size_type;
    

    然后你就可以使用pos_type

    【讨论】:

      【解决方案3】:

      不能使用auto返回类型推导。

      对于,读懂你的想法,你移植了这个:

      inline unsigned long int determineFirstPosition(const std::string s)
      {
          for (unsigned long int i = 0; i < s.length(); ++i)
          {
              switch (s.at(i))
              {
                  case ' ':
                  {
                      break;
                  }
                  default:
                  {
                      return i;
                  }
              }
          }
      
          return s.length();
      }
      

      inline auto determineFirstPosition(const std::string s)
      {
          for (auto i = 0; i < s.length(); ++i)
          {
              switch (s.at(i))
              {
                  case ' ':
                  {
                      break;
                  }
                  default:
                  {
                      return i;
                  }
              }
          }
      
          return s.length();
      }
      

      在这种情况下,您的错误是

          for (auto i = 0; i < s.length(); ++i)
      

      因为auto i = 0int,而不是s.length() 的类型。

          for (decltype(s.length()) i = 0; i < s.length(); ++i)
      

      如果你想避免在这里命名类型。

      或者,您可以使用std::string::size_type。或者,您可以编写一个实用程序,让您 for(:) 将索引添加到某些内容中;

      template<class T> struct tag_t {using type=T;};
      
      template<class X> using block_deduction = typename tag_t<X>::type;
      
      template<class It>
      struct range_t {
        It b, e;
        It begin() const { return b; }
        It end() const { return e; }
      };
      template<class S>
      struct indexer_t {
        S s;
        void operator++(){++s;}
        S operator*() const{ return s; }
        friend bool operator==( indexer_t const& lhs, indexer_t const& rhs ) {
          return lhs.s == rhs.s;
        }
        friend bool operator!=( indexer_t const& lhs, indexer_t const& rhs ) {
          return lhs.s != rhs.s;
        }
      };
      template<class S>
      range_t<indexer_t<S>> indexes( block_deduction<S> start, S finish ) {
        return {{std::move(start)}, {std::move(finish)}};
      }
      template<class C>
      auto indexes_into( C&& c ) {
        return indexes( 0, c.size() );
      }
      

      所有这些都可以让你做到:

      for( auto i : indexs_into(s) )
      

      而不是

      for (unsigned long int i = 0; i < s.length(); ++i)
      

      Live example.

      (作为附带奖励,

      template<class C>
      auto iterators_into( C& c ) {
        return indexes( c.begin(), c.end() );
      }
      

      也很有用,允许您将所有有效的迭代器迭代到一个容器中,而无需手动编写for(;;) 循环)

      【讨论】:

      • 这很有趣。我不会使用这个(至少现在不会),但我一定会研究这个方法。这里有一些语言功能对我来说是新的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-10-04
      • 1970-01-01
      • 2019-11-04
      • 2013-11-16
      相关资源
      最近更新 更多