4

tr1 サービス パックと Intel C++ Compiler 11.1.071 [IA-32] で Visual Studio 2008 を使用すると、これは私の他の質問に関連しています。

Rubyバージョンのように機能するc ++の機能マップを作成しようとしています

strings = [2,4].map { |e| e.to_s }

だから私は VlcFunctional 名前空間で次の関数を定義しました

template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
    vector<U> transformedValues(container.size());
    int index = -1; 
    BOOST_FOREACH(const auto& element, container)
    {
        transformedValues.at(++index) = f(element);
    }
    return transformedValues; 
}

これを次のように呼び出すことができます (関数テンプレートの引数は明示的に定義されていることに注意してください)。

vector<int> test;
test.push_back(2); test.push_back(4); 
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
    return ToString(i);
});

またはそのように (関数テンプレートの引数は明示的に定義されていないことに注意してください)

std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);

しかし決定的に、これは好きではありません

vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });

テンプレート引数を明示的に定義しないと、どのテンプレートを使用するかがわからず、コンパイル エラーが発生します。

 ..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)

テンプレート引数を定義する必要があるため、構文がはるかにかさばります。呼び出しサイトでのクラフトを最小限に抑えることを目指しています-変換方法がわからない理由についてのアイデアはありますか? これはコンパイラの問題ですか、それとも言語がこのタイプのテンプレート引数の推論を許可していませんか?

4

1 に答える 1

4

問題は、ラムダがstd::function変換できても偶数ではないことです。型引数を推測するとき、コンパイラは実際に提供された引数に対して変換を実行できません。Uコンパイラに型を検出させ、コンパイラが推測できるように 2 番目の引数を解放する方法を探します。

template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...

さて問題はXXXに何を書くかです。私はあなたと同じコンパイラを持っていません。C++0x のすべての機能はまだ少しトリッキーです。私は最初に使用しようとしますdecltype

template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...

または、コンパイラがまだサポートしていない場合は、型の特徴かもしれませdecltypeん。

また、あなたが書いているコードは、C++ では非常に一義的であることに注意してください。通常、コンテナを操作する場合、関数はイテレータの観点から実装され、マップ全体は基本的に古いstd::transform:

std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );

std::transform関数の C++ バージョンはどこにありますかmap。構文はより面倒ですが、利点は、それを任意のコンテナーに適用し、他のコンテナーに出力を生成できることです。そのため、変換されたコンテナーは に固定されませんstd::vector

編集:おそらく現在のコンパイラ サポートで実装する方が簡単な 3 番目のアプローチは、テンプレート引数としてラムダの戻り値の型のみを手動で提供し、コンパイラに残りを推測させることです。

template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
   std::vector<LambdaReturn> ret;
   std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
   return ret;
}
int main() {
   std::vector<int> v{ 1, 2, 3, 4, 5 };
   auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}

関数にシンタックス シュガーを追加したい場合でも、map既存の機能を使用できる場合は手動で実装する必要はありません。

于 2010-09-09T08:06:47.440 に答える