web-dev-qa-db-ja.com

テンプレート引数としてのテンプレート関数

C++で一般的な方法で何かを実装する方法が混乱しました。少し複雑なので、少しずつ説明させていただきます。


このようなコードを検討してください。

_void a(int) {
    // do something
}
void b(int) {
    // something else
}


void function1() {
    a(123);
    a(456);
}
void function2() {
    b(123);
    b(456);
}

void test() {
    function1();
    function2();
}
_

_function1_と_function2_が同じことをしていることは簡単にわかりますが、異なる部分は内部関数だけです。

したがって、コードの冗長性を回避するために、functionを汎用にしたい。関数ポインタまたはテンプレートを使用してそれを行うことができます。とりあえず後者を選びましょう。 コンパイラーは確実に関数をインライン化できるので、より良いと思います-私は正しいですか?コンパイラーは、関数ポインターを介して行われた場合でも呼び出しをインライン化できますか?これは副次的な質問です。

OK、元のポイントに戻ります...テンプレートを使用したソリューション:

_void a(int) {
    // do something
}
void b(int) {
    // something else
}

template<void (*param)(int) >
void function() {
    param(123);
    param(456);
}

void test() {
    function<a>();
    function<b>();
}
_

大丈夫。しかし、問題が発生しています。ab自体がジェネリックである場合でも、それを実行できますか?

_template<typename T>
void a(T t) {
   // do something
}

template<typename T>
void b(T t) {
   // something else
}

template< ...param... > // ???
void function() {
    param<SomeType>(someobj);
    param<AnotherType>(someotherobj);
}

void test() {
    function<a>();
    function<b>();
}
_

テンプレートパラメータは次のいずれかになります。

  • タイプ、
  • テンプレートタイプ、
  • タイプの値。

それらのどれも私の状況をカバーしていないようです。したがって、私の主な質問は次のとおりです。これを解決するにはどうすればよいですか。つまり、最後の例でfunction()を定義しますか?

(はい、関数ポインターはこの場合の回避策のようです-インライン化することもできます-しかし、私はこのクラスの問題の一般的な解決策を探しています)

33
Kos

テンプレートでこの問題を解決するには、テンプレートテンプレートパラメータを使用する必要があります。残念ながら、最初にインスタンス化する必要があるため、テンプレートテンプレート関数を型として渡すことはできません。ただし、ダミー構造には回避策があります。次に例を示します。

template <typename T>
struct a {

    static void foo (T = T ())
    {
    }

};

template <typename T>
struct b {

    static void foo (T = T ())
    {
    }

};

struct SomeObj {};
struct SomeOtherObj {};

template <template <typename P> class T>
void function ()
{
    T<SomeObj>::foo ();
    T<SomeOtherObj>::foo ();
}

int main ()
{
    function<a>();
    function<b>();
}
27
user405725

これが方法です。最善ではないかもしれませんが、機能します。

template <typename T, T param>
void function() {
    param(123);
    param(456);
}

void test()
{
    function< void(*)(int), a<int> >(); // space at end necessary to compiler
    function< void(*)(int), b<int> >(); // because the C++ grammar is ambiguous
}

インライン化されるかどうかはコンパイラーによって異なりますが、インライン化されていない場合はかなり驚きます。

編集:さて、今日は少し離れていて、パラメーターがさまざまなタイプである部分を見逃しました。私の悪い。

テンプレートを使用してこれを行うには注意が必要な方法があるかもしれませんが、これは私が考えることができる最も簡単な方法です。

#define function(x) do { x<thing1>(obj1); x<thing2>(obj2) } while(0)

私は知っている、私は知っている、「マクロは悪だ」何とか何とか何とか。できます。 functionを例よりも複雑にする必要がある場合は、問題が発生する可能性がありますが、私が思いついたものよりもはるかに簡単です。

0
Chris Lutz

C++ 11のラムダを使用すると、次のことができます。

template<typename T> void a(T t) { /* do something */}
template<typename T> void b(T t) { /* something else */ }

template <typename F>
void function(F&& f) {
    f(someobj);
    f(someotherobj);
}

void test() {
    // For simple cases, auto&& is even probably auto or const auto&
    function([](auto&& t){ a(t); });
    function([](auto&& t){ b(t); });

    // For perfect forwarding
    function([](auto&& t){ a(std::forward<decltype(t)>(t)); });
    function([](auto&& t){ b(std::forward<decltype(t)>(t)); });
}

関数ポインターを介して呼び出された場合でも、コンパイラーは呼び出しをインライン化できますか?

可能ですが、実際にはもっと複雑で、ファンクターやテンプレートよりも頻繁に失敗する可能性があります。

0
Jarod42