4

Pythonの関数マップを C++11 で実装しようとしています。どの種類の呼び出し可能オブジェクトでも機能するようですが、関数テンプレートで機能させたい場合は、テンプレート タイプ パラメーターを指定する必要があります。例:

#include <iostream>
#include <list>

template<typename T>
T abs(T x)
{
    return x < 0 ? -x : x;
}

int main()
{
    std::list<int> li = { -1, -2, -3, -4, -5 };
    for (auto i: map(&abs<int>, li))
    {
        std::cout << i << std::endl;
    }
}

正常に動作しますが、関数の 2 番目の引数からパラメーターを推測してint、次のように記述できるようにしたいと考えています。

for (auto i: map(&abs, li))
{
    std::cout << i << std::endl;
}

私のmap関数は次のように書かれています:

template<typename Callable, typename Container>
auto map(const Callable& function, Container&& iter)
    -> MapObject<Callable, Container>
{
    return { function, std::forward<Container>(iter) };
}

whereMapObjectは実装の一部であり、ここでは実際の問題ではありません。Callableオブジェクトのテンプレート型をオブジェクトから推測できるように、その定義を変更するにはどうすればよいContainerですか? たとえば、a が与えられたときに、与えられたmapものを使用する必要があることをどのように知ることができますか?abs<int>abslist<int>

4

2 に答える 2

5

正常に動作しますが、関数の 2 番目の引数から int パラメーターを推測して、次のように記述できるようにしたいと考えています。

for (auto i: map(&abs, li))
{
    std::cout << i << std::endl;
}

問題は、absこれは関数ではなく関数テンプレートであるため、address-of absはありますが&abs<int>abs<int>(特殊化) は実際には関数 (テンプレートから生成される) であるため、ありません。

ここで問題は、あなたが本当に解決したいことです。特に、Python が動的型付け言語であるのに対し、C++ は静的型付け言語であることを認識しなければなりません。ここでさまざまなレベルで何を達成しようとしているのか、私にはわかりません。たとえば、Python の関数には、C++mapの同等の関数があります。std::transform

a = [ 1, 2, 3 ]
a = map(lambda x: 2*x, a)

std::vector<int> v{1,2,3};
std::transform(v.begin(),v.end(),v.begin(),[](int x){ return 2*x; });

Python では別のコンテナーが作成されますが、C++ ではtransform反復子レベルで動作し、コンテナーが認識されないため、少しごまかしましたが、同様に同じ効果を得ることができます。

std::vector<int> v{1,2,3};
std::vector<int> result;
// optionally: result.reserve(v.size());
std::transform(v.begin(),v.end(),
               std::back_inserter(result),
               [](int x) { return 2*x; });

他の言語のイディオムを実装しようとするよりも、その言語のイディオムを学ぶことをお勧めします...

ところで、関数に渡されるファンクターの型をユーザーに指定してもらいたい場合はmap、テンプレートの名前を渡すだけで、必要な特殊化をコンパイラーに判断させることができます。

template <typename Container>
auto map(Container && c, 
         typename Container::value_type (*f)(typename Container::value_type))
     -> MapObject<Callable<T>,Container>;
template <typename T>
T abs(T value);

int main() {
   std::vector<int> v{1,2,3,4};
   map(v,abs);
}

これは、関数ポインターと具象型のみを受け入れるため (これは よりも一般的ではありませんstd::transform)、コンパイラがabs( なしで&) テンプレートに解決するのを認識したときに機能するため、実行しようとしていたものよりも一般的ではありません。したがって、一連の専門分野に。次に、予想される型を使用して 1 つの特殊化を選択し、それを渡します&abs<int>。この場合、コンパイラは暗黙的に実行します。

別のより一般的な代替手段は、関数ではなくファンクターを使用することです。これを念頭に置いて、次のように定義できますabs

struct abs {
   template <typename T>
   T operator()(T t) { ...}
};

そして、関数ポインターの代わりにファンクターのコピーを渡します。absオブジェクトを関数に渡す場所で使用するオーバーロードを決定する必要はありませんmap。使用する場合のみです。呼び出し側は次のようになります。

for (auto& element : map(container,abs()))

余分な括弧のセットは、型のオブジェクトを作成しabsて渡す場所です。

全体として、私はこれを避けようとします。これは楽しい作業であり、おそらく適切な解決策にたどり着くことができますが、それは難しく、かなりの C++ の専門知識が必要になります。言語でサポートされていないため、言語内で機能し、さまざまな機能や構文で妥協が必要なものを設計する必要があります。選択肢を知ること自体が困難な問題であり、妥協点を理解することはさらに困難であり、優れた解決策にたどり着くのははるかに困難です。そして、良い解決策は、同等の慣用的な C++ コードよりもおそらく悪いでしょう。

C++ でプログラミングする場合は、C++ をプログラミングします。C++ コンパイラを使用してPythonをコーディングしようとすると、おそらく C++ の苦痛と Python のパフォーマンスが得られます。

于 2013-04-27T20:27:54.083 に答える
-1

Callable がテンプレートであることを指定したことがないため、推測できません。Callable をテンプレート テンプレート パラメーターにすると、その型が推測されます。

template<template <typename T> typename Callable, typename Container>
auto map(const Callable<T>& function, Container&& iter)
    -> MapObject<Callable<T>, Container>
{
    return { function, std::forward<Container>(iter) };
}

ただし、まだインスタンス化されていないテンプレートのアドレスを取得できないため、噛まれる可能性があります。ただし、なぜアドレスオブが必要なのかわかりません...

于 2013-04-27T19:24:01.853 に答える