web-dev-qa-db-ja.com

「transform(s.begin()、s.end()、s.begin()、tolower)」を正しくコンパイルできないのはなぜですか?

コードを考える:

_#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
     string s("ABCDEFGHIJKL");
     transform(s.begin(),s.end(),s.begin(),tolower);
     cout<<s<<endl;
}
_

エラーが発生します:

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> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)の呼び出しに一致する関数がありません

"unresolved overloaded function type"はどういう意味ですか?

tolowerを私が作成した関数に置き換えても、エラーは発生しなくなりました。

29
liu

::tolowerを使用してみてください。これで問題が解決しました。

27
davka

問題はおそらくtolowerの複数のオーバーロードに関連しており、コンパイラーは1つを選択できません。これを修飾して特定のバージョンを選択するか、明確にするためにキャストされた関数ポインターを提供する必要がある場合があります。 tolower関数は、<locale>ヘッダーと<cctype>に存在できます(複数の異なるオーバーロード)。

試してください:

int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );

これはキャストを使用して1行で行うことができますが、おそらく読みにくいでしょう。

transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );

最悪のものから始まり、最高のものに移動するオプションのリストを見てみましょう。それらをここにリストし、以下で説明します。

  1. transform(cbegin(s), cend(s), begin(s), ::tolower)
  2. transform(cbegin(s), cend(s), begin(s), static_cast<int(*)(int)>(tolower))
  3. transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })

あなたの質問のコード、transform(s.begin(), s.end(), s.begin(), tolower)は次のようなエラーを生成します:

transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)の呼び出しに一致する関数がありません

「未解決のオーバーロードされた関数型」を取得した理由は、tolower名前空間に2つのstdsがあるためです。

  1. localeライブラリは template <typename T> T tolower(T, const locale&) を定義します
  2. cctypeライブラリは int tolower(int) を定義します

1davkaが提供するソリューション です。 localetolowerがグローバル名前空間で定義されていないという事実を利用して、エラーに対処します。

状況によっては、localetolowerを検討する価値があります。 tolowersの比較は、次の場所にあります。 C++ではどちらが低いですか?


残念ながら1は、cctypetolowerがグローバル名前空間で定義されていることに依存しています。それがそうではないかもしれない理由を見てみましょう:

_#include <cctype>_をC++で廃止されたため、_#include <ctype.h>_を正しく使用しています: http://en.cppreference.com/w/cpp/header

ただし、C++標準では、ヘッダーの宣言のD.3 [depr.c.headers] 2に次のように記載されています。

これらの名前が最初に名前空間stdの名前空間スコープ(3.3.6)内で宣言または定義され、次に明示的なusing宣言(7.3.3)によってグローバル名前空間スコープに注入されるかどうかは指定されていません。

したがって、コードが実装に依存しないことを保証できる唯一の方法は、_namespace std_のtolowerを使用することです。 2DavidRodríguez-dribeasが提供するソリューション です。 _static_cast_ ができることを利用しています:

特定の型への関数からポインターへの変換を実行して、関数のオーバーロードを明確にするために使用されます

先に進む前に、int (*)(int)が少しわかりにくい場合は、関数ポインタの構文 here について詳しく読むことができるとコメントさせてください。


悲しいことに 他の1つの問題 が存在する場合、tolowerの入力引数があります:

Unsigned charとして表現できず、EOFと等しくない、動作は未定義

タイプstringのエレメントを使用するcharを使用しています。特にcharの標準状態は7.1.6.2 [dcl.type.simple] 3です。

char型のオブジェクトが符号付きまたは符号なしの数量として表されるかどうかは、実装によって定義されます。 signed指定子はcharオブジェクトを強制的に署名します

したがって、実装でcharが_signed char_を意味するように定義した場合、12は、負の数値に対応するすべての文字に対して未定義の動作になります。 (ASCII文字エンコードが使用されている場合、負の数に対応する文字は 拡張ASCII です。)

未定義の動作は、tolowerに渡す前に入力を_unsigned char_に変換することで回避できます。 3は、値によって_unsigned char_を受け入れるラムダを使用してそれを実行し、それをtolowerに渡して暗黙的にintに変換します。

文字エンコーディングに関係なく、すべての準拠した実装で定義済みの動作を保証するには、transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })または類似のものを使用する必要があります。

19
Jonathan Mee

デビッドはすでに問題、すなわち以下の間の矛盾を特定しました:

  • _<cctype>_int tolower(int c)
  • _<locale>_template <typename charT> charT tolower(charT c, locale const& loc)

最初のものを使用する方がはるかに簡単ですが、符号付き文字で小文字のASCII(0〜127)以外のものを処理するとすぐに(残念ながら)未定義の動作になります。 ところで、私はcharを符号なしとして定義することをお勧めします。

テンプレートのバージョンはいいですが、2番目のパラメーターを提供するためにbindを使用する必要があり、醜いことにバインドされています...

では、 Boost String Algorith mライブラリを紹介してもよいですか?

そしてもっと重要なこと: _boost::to_lower_ :)

_boost::to_lower(s);
_

表現力が望ましい。

6
Matthieu M.

Gcc 4.2.1の<ctype>ヘッダーを参照すると、次のように表示されます。

// -*- C++ -*- forwarding header.

// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
// Free Software Foundation, Inc.

...

#ifndef _GLIBCXX_CCTYPE
#define _GLIBCXX_CCTYPE 1

#pragma GCC system_header

#include <bits/c++config.h>
#include <ctype.h>

// Get rid of those macros defined in <ctype.h> in lieu of real functions.
#undef isalnum
#undef isalpha

...

#undef tolower
#undef toupper

_GLIBCXX_BEGIN_NAMESPACE(std)

  using ::isalnum;
  using ::isalpha;

...

  using ::tolower;
  using ::toupper;

_GLIBCXX_END_NAMESPACE

#endif

したがって、tolowerは、std<cctype>から)とルート(<ctype.h>から)の両方のネームスペースに存在するようです。 #pragmaの意味がわかりません。

4
Mike DeSimone