Переносимость и длина std :: string в Windows и Linux - PullRequest
0 голосов
/ 20 ноября 2018

У меня проблемы с переносимостью при длине 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 int s на 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 (как показано выше)?

Ответы [ 3 ]

0 голосов
/ 20 ноября 2018

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 pos_type = std::string::size_type;

и тогда вы просто используете pos_type.

0 голосов
/ 20 ноября 2018

не может использовать 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 = 0 - это int, а не тип 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)

Живой пример .

(в качестве дополнительного бонуса,

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

также полезен, позволяя вам перебирать все действительные итераторы в контейнер без необходимости вручнуюнаписание for(;;) петли)

0 голосов
/ 20 ноября 2018

Посмотрев документацию std::string, вы увидите, что тип называется

std::string::size_type

, так что просто используйте его.Вам не нужно знать или угадывать, какой тип примитива это typedef из - у вас уже есть пригодное имя, которое гарантированно будет правильным.

...