64 votes

std::transform() et toupper(), pas de fonction assortie

J'ai essayé le code à partir de cette question, C++ std::transform() et toupper() ..pourquoi est-ce un échec?

#include <iostream>
#include <algorithm>

int main() {
  std::string s="hello";
  std::string out;
  std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper);
  std::cout << "hello in upper case: " << out << std::endl;
}

Théoriquement, il devrait avons travaillé comme il est l'un des exemples en Josuttis livre, mais il ne compile pas http://ideone.com/aYnfv.

Pourquoi GCC se plaindre:

no matching function for call to ‘transform(
    __gnu_cxx::__normal_iterator<char*, std::basic_string
        <char, std::char_traits<char>, std::allocator<char> > >, 
    __gnu_cxx::__normal_iterator<char*, std::basic_string
        <char, std::char_traits<char>, std::allocator<char> > >, 
    std::back_insert_iterator<std::basic_string
        <char, std::char_traits<char>, std::allocator<char> > >,
    <unresolved overloaded function type>)'

Suis-je manqué quelque chose? Est-il GCC problème lié?

80voto

Nawaz Points 148870

Suffit d'utiliser ::toupper au lieu de std::toupper. C'est, toupper défini dans l'espace de noms global, au lieu de celui défini à l' std d'espace de noms.

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);

Son travail : http://ideone.com/XURh7

La raison pourquoi votre code ne fonctionne pas : il y a une autre fonction surchargée toupper en l'espace de noms std qui est à l'origine de problème lors de la résolution du nom, parce que le compilateur est incapable de décider qui surcharge vous faites allusion, lorsqu'il vous suffit de passer std::toupper. C'est pourquoi le compilateur dit unresolved overloaded function type dans le message d'erreur qui indique la présence de surcharge(s).

Afin d'aider le compilateur à régler à la bonne surcharge, vous avez à jeter std::toupper comme

(int (*)(int))std::toupper

Qui est, la suivante devrait fonctionner:

//see the last argument, how it is casted to appropriate type
std::transform(s.begin(), s.end(), std::back_inserter(out),(int (*)(int))std::toupper);

Vérifiez-le vous-même: http://ideone.com/8A6iV

49voto

Problème

std::transform(
    s.begin(),
    s.end(),
    std::back_inserter(out),
    std::toupper
);

no matching function for call to ‘transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)'

C'est une erreur trompeur; la partie la plus intéressante n'est pas qu'il n'y a "pas de fonction de correspondance" pour l'appel, mais pourquoi il n'y a pas de fonction correspondant.

Le pourquoi c'est que vous êtes de passage d'une fonction de référence d'un "<unresolved overloaded function type>" comme argument, et GCC préfère erreur sur l'appel, plutôt que sur ce surcharge échec de la résolution.


Explication

Tout d'abord, vous devriez considérer comment la bibliothèque C est héritée en C++. <ctype.h> a une fonction int toupper(int).

C++ est l'héritier de cette:

[n3290: 21.7/1]: Tables 74, 75, 76, 77, 78, et 79 décrire les en-têtes <cctype>, <cwctype>, <cstring>, <cwchar>, <cstdlib> (conversions de caractères), et <cuchar>, respectivement.

[n3290: 21.7/2]: Le contenu de ces en-têtes sont les mêmes que la Bibliothèque Standard C-têtes <ctype.h>, <wctype.h>, <string.h>, <wchar.h>, et <stdlib.h> et le C Unicode TR l'en-tête <uchar.h>, respectivement [..]

[n3290: 17.6.1.2/6]:Noms qui sont définis comme des fonctions en C être définis comme des fonctions en C++ de la bibliothèque standard.

Mais à l'aide de <ctype.h> est obsolète:

[n3290: C.3.1/1]: Pour la compatibilité avec la bibliothèque Standard C, le C++ de la bibliothèque standard fournit le 18 C-têtes (D. 5), mais leur utilisation est déconseillé en C++.

Et le chemin pour accéder à la C toupper est à travers le C++ rétro-compatibilité-tête <cctype>. Pour ces en-têtes, le contenu est déplacé ou copié (en fonction de la mise en œuvre) dans l' std d'espace de noms:

[n3290: 17.6.1.2/4]: [..] Dans la norme C++ de la bibliothèque, toutefois, les déclarations (sauf pour les noms qui sont définis comme des macros en C) sont dans l' espace de noms de la portée (3.3.6) de l'espace de noms std. Il n'est pas spécifié si ces noms sont d'abord déclarés dans l'espace de noms global portée et sont ensuite injectés dans l'espace de noms std explicites à l'aide de déclarations (7.3.3).

Mais la bibliothèque C++ introduit aussi une nouvelle, locale spécifique au modèle de fonction dans l'en-tête <locale>, qui est également appelés toupper (bien sûr, dans l'espace de noms std):

[n3290: 22.2]: [..] template <class charT> charT toupper(charT c, const locale& loc); [..]

Donc, lorsque vous utilisez std::toupper, il y a deux surcharges de choisir à partir. Puisque vous n'avez pas demander à GCC de la fonction que vous souhaitez utiliser, la surcharge ne peut pas être résolu et votre appel à l' std::transform ne peut pas être terminée.


La disparité

Maintenant, les OP de la question d'origine n'a pas ce problème. Il n'a pas la version locale de l' std::toupper dans l'étendue, mais encore une fois, vous n'avez pas #include <locale> soit!

Cependant:

[n3290: 17.6.5.2]: D'un en-tête C++ peut inclure d'autres C++ en-têtes.

Donc, il arrive que soit votre <iostream> ou votre <algorithm>, ou les en-têtes de ces en-têtes inclure, ou les en-têtes de ces en-têtes include (etc), de conduire à l'inscription de l' <locale> sur votre mise en œuvre.


Solution

Il y a deux solutions à cela.

  1. Vous pouvez fournir une clause de conversion contraindre le pointeur de fonction en se référant à la surcharge de travail que vous souhaitez utiliser:

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       (int (*)(int))std::toupper  // specific overload requested
    );
    
  2. Vous pouvez supprimer la version locale de la surcharge de jeu en utilisant explicitement le global toupper:

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       ::toupper                  // global scope
    );
    

    Toutefois, rappelons que si oui ou non cette fonction en <cctype> est disponible n'est pas spécifié ([17.6.1.2/4]), et à l'aide de <ctype.h> est obsolète ([C.3.1/1]).

    Ainsi, ce n'est pas l'option que je vous recommande.


(Note: je méprise l'écriture crochets comme si elles faisaient partie des noms d'en-tête, elles font partie de l' #include de la syntaxe, pas de noms d'en-tête — mais je l'ai fait ici, par souci de cohérence avec la FDIS citations; et, pour être honnête, c' est plus clair...)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X