4

各コンポーネントで計算を行った後、多くの STL ベクトルを画面に出力する必要があるプログラムがあります。だから私はこのような関数を作成しようとしました:

template <typename a> 
void printWith(vector<a> foo, a func(a)){
  for_each(foo.begin(), foo.end(), [func](a x){cout << func(x) << " "; });
}

そして、次のように使用します。

int main(){
  vector<int> foo(4,0);
  printWith(foo, [](int x) {return x + 1;});
  return 0;
}

printWith残念ながら、呼び出しの中に入れたラムダ式の型に関するコンパイル エラーが発生しました。

g++ -std=gnu++0x -Wall -c vectest.cpp -o vectest.o
vectest.cpp: In function ‘int main()’:
vectest.cpp:16:41: error: no matching function for call to ‘printWith(std::vector<int>&, main()::<lambda(int)>)’
vectest.cpp:10:6: note: candidate is: void printWith()
make: *** [vectest.o] Error 1

もちろん、私がそうするなら:

int sumOne(int x) {return x+1;}

その後printWith(foo, sumOne);、意図したとおりに動作します。ラムダ式の型は、推論された戻り値の型を持つ関数の型になると思いました。また、通常の関数に適合できる場所ならどこにでもラムダを適合できると考えました。どうすればこれを機能させることができますか?

4

4 に答える 4

6

以下は私にとってはうまくいきます:

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename F>
void printWith(vector<a> foo, F f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

テスト:

$ g++-4.5 -std=gnu++0x -Wall test.cpp
$ ./a.out                            
2 3 4 5 6 

または、ラムダ キャプチャのないクロージャ型を関数ポインタに暗黙的に変換できるという事実を利用することもできます。これにより、元のコードに近くなり、関数テンプレートのインスタンス化の数も削減されます (元のソリューションでは、関数テンプレートを別の関数オブジェクト タイプで使用するたびに新しいインスタンス化が行われます。ただし、そうではないことに注意してください)。printWith関数は非常に短く、おそらく常にインライン化されるため、この特定のケースではあまり重要ではありません):

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, b f(a)){
  for_each(foo.begin(), foo.end(), [=](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith<int, int>(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

残念ながら、暗黙的な変換は、テンプレート引数の演繹ではうまく機能しません。ご覧のとおり、 の呼び出しでテンプレート引数を指定する必要がありましたprintWith

もう 1 つの方法は、 を使用することstd::functionです。これは、テンプレートのインスタンス化の数を最小限に抑えるのにも役立ち、lambda-capture を使用したラムダ式に対しても機能しますが、テンプレートの引数推定に関して同じ問題があります。

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, std::function<b(a)> f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  int y = 1;
  printWith<int, int>(foo, [&](int x) { return x + y; });
  std::cout << '\n';
  return 0;
}
于 2011-06-06T16:03:27.860 に答える
4

問題が発生している理由は、関数を使用しようとしているからです。フリー関数には、いかなる種類の関数オブジェクトとも交換できない特定の表現 (関数ポインターとして) があります。関数ポインター (基本的にここにあるもの) は避ける必要があります。テンプレートで指定されたタイプの関数オブジェクトを直接取得する必要があります。

template <typename a, typename Func> 
void printWith(vector<a> foo, Func func){
  for_each(foo.begin(), foo.end(), [&](a x){cout << func(x) << " "; });
}

または、 などの多相関数オブジェクトを使用しstd::functionます。

template<typename a>
void printWith(vector<a> foo, std::function<string(const a&)> func) {
    for_each(foo.begin(), foo.end(), [&](a x) { cout << func(x) << " "; });
}
于 2011-06-06T16:08:08.490 に答える
2
void printWith(vector<a> foo, b func(a)){

これは間違っています。それを行うことはできません。これにより、コンパイラはこのコードが有効ではないため、考慮されなくなります。

これを修正するには、次の 2 つの方法があります。

1) パラメーターの型を要求するのではなく、ファンクターを要求するだけです。

   void printWith(vector<a> foo, b func ){ // keep the rest of the code the same

aとにかく func が as パラメーターを取らない場合、関数の残りの部分はコンパイルされません。

2) ファンクターの型を強制する:

template <typename a> 
void printWith(vector<a> foo, std::function< void (a) > func ){

次に、関数ポインターを使用していた場合と同様です。コンパイル時の最適化はありません (または少ない) が、少なくともファンクター シグネチャを強制します。詳細については、std::function または boost::function を参照してください。

于 2011-06-06T15:54:12.123 に答える
1

これが機能しない理由は、テンプレート引数の推定と暗黙の変換を混在させているためです。控除を取り除くと、次のように機能します。

printWith<int>(foo, [](int x) {return x + 1;});

funcただし、他の人が推奨するように、 type を別のテンプレートパラメーターにする方がよいでしょう (printWith 内) 。

一方、本当にこのタイプに制約を追加したい場合は、SFINAE (ソフト エラーの場合) またはstatic_assert(ハード エラーの場合) を使用して行うより良い方法があります。

例えば:

// A constraints metafunction
template<typename T, typename Element>
struct is_element_printer
    : std::is_convertible<T, Element (*)(Element)>
{};

ここで、is_element_printer<T, Element>::valuetrue暗黙T的に に変換されElement (*)(Element)ます。これは説明目的でのみ使用しており、実際に使用することはお勧めできません。関数ポインターではない多くの状況で「要素プリンター」と見なされるものはたくさんあります。私がこれを行っているのstd::is_convertibleは、からすぐに入手でき、<type_traits>他にもっと明白なテストが利用できないからです。自分で書くべきです。

それで:

template<typename Container, typename Functor>
void
printWith(Container&& container, Functor&& functor)
{
    // avoid repetition
    typedef typename std::decay<Container>::type::value_type value_type;

    // Check our constraints here
    static_assert(
        std::is_element_printer<
            typename std::decay<Functor>::type,
            value_type
        >::value,
        "Descriptive error message here"
    );

    // A range-for is possible instead
    std::for_each(container.cbegin(), container.cend(), [&functor](value_type const& v)
    { std::cout << functor(v) << ' '; });
}
于 2011-06-06T17:19:33.093 に答える