Je rencontre des problèmes de portabilité avec la longueur de C ++ 11 std::string. Sous Windows, c'est long long unsigned int mais sous Linux et Mac, c'est long unsigned int. Je crois comprendre que l'utilisation de auto est une approche standard pour résoudre des problèmes comme celui-ci, mais j'ai du mal à trouver un moyen portable d'exposer ces attributs via une interface de classe.


La classe suivante se compile et s'exécute sans problème sur Linux GCC 7.3.0 (et aussi sur MacOS):

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

Mais sous Windows (g ++ 8.1.0 MinGW-W64 x86_64-posix-seh-rev0), j'obtiens l'erreur de compilation suivante:

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;
}

J'ai essayé de changer tous les unsigned long int en auto, et avec -std=c++11, j'obtiens les erreurs suivantes:

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

Lorsque j'utilise auto et compile --std=c++14, j'obtiens l'erreur suivante:

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();
                     ^

Question: Comment puis-je écrire du code C ++ 11 portable (Linux, Windows) qui évite les conversions de type dans les types de données STL comme std::string (comme illustré ci-dessus)?

0
vallismortis 20 nov. 2018 à 17:54

3 réponses

Meilleure réponse

std::string fournit des types publics, comme std::string::size_type, que vous pouvez utiliser pour définir votre fonction. Vous pouvez définir votre fonction determineFirstPosition comme

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();
}

Et si vous ne voulez pas répéter std::string::size_type partout, vous pouvez ajouter une déclaration using à votre classe pour raccourcir le nom comme

using pos_type = std::string::size_type;

Et vous utiliseriez simplement pos_type.

4
NathanOliver 20 nov. 2018 à 14:59

ne peut pas utiliser la déduction de type de retour auto.

Pour , en lisant votre esprit, vous avez porté ceci:

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();
}

Dans ce cas, votre erreur était

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

Car auto i = 0 est un int, pas le type de s.length().

Faire

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

Si vous souhaitez éviter de nommer des types ici.

Vous pouvez également utiliser std::string::size_type. Sinon, vous pouvez écrire un utilitaire pour vous permettre de for(:) sur les index dans quelque chose;

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() );
}

Tout cela vous permet de faire:

for( auto i : indexs_into(s) )

Au lieu de

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

Exemple en direct.

(en prime,

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

Est également utile, vous permettant de parcourir tous les itérateurs valides dans un conteneur sans écrire manuellement une boucle for(;;))

2
Yakk - Adam Nevraumont 20 nov. 2018 à 15:10

En consultant la std::string documentation, vous pouvez voir que le type est appelé

std::string::size_type

Alors utilisez-le. Vous n'avez pas besoin de savoir ou de deviner de quel type primitif il s'agit d'un typedef de - vous avez déjà un nom utilisable qui est garanti correct.

5
Useless 20 nov. 2018 à 14:59