6

私はいくつかの異なる機能を共有するクラスを書いていますstd::function(または少なくともクラスは多くの点で似ています)。std::functionはテンプレート パラメーター (つまり ) を指定することでインスタンス化されることはご存知のとおりstd::function<void (std::string&)>、私のクラスでも同じです。ただし、例外があります。戻り値が void ( ) の場合、クラスで単一の関数を特殊化したいと考えていますstd::function<"return value" ("parameters">。コンパイル時にこれを行う必要がありますが、本来の動作をさせることができません。説明のためのテストコードを次に示します。

#include <iostream>
#include <type_traits>

template <typename T> class Test { };

template <typename Ret, typename... Args>
class Test<Ret (Args...)>
{
public:
    Ret operator()(Args...)
    {
        if(std::is_void<Ret>::value)
        {
             // Do something...
        }

        else /* Not a void function */
        {
            Ret returnVal;
            return returnVal;
        }
    }
};

int main(int argc, char * argv[])
{
    Test<void (char)> test;
    test('k');
}

ご覧のとおり、上記のテストでコンパイラが「else」ブランチを削除しない場合、私のコードは void 値 (つまりvoid returnVal;) を作成しようとします。問題は、コンパイラがブランチを削除しないため、コンパイラ エラーが発生することです。

./test.cpp: 'Ret Test::operator()(Args ...) [with Ret = void; のインスタンス化中。Args = {char}]': ./test.cpp:27:10: ここから必要 ./test.cpp:18:8: エラー: 変数またはフィールド 'returnVal' は void を宣言しました ./test.cpp:19:11 : エラー: 'void' を返す関数の値を含む return-statement [-fpermissive]

通常はstd::enable_ifと組み合わせて使用​​しますが、問題は、関数テンプレートではなく、クラスstd::is_voidテンプレートに特化したいということです。

template <typename Ret, typename... Args>
class Test<Ret (Args...)>
{
public:
    typename std::enable_if<!std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...)
    {
        Ret returnVal;
        return returnVal;
    }

    typename std::enable_if<std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...)
    {
        // It's a void function
        // ...
    }
};

代わりに上記のコードを使用すると、さらに多くのエラーが発生し、解決策がありません

./test.cpp:11:2: error: expected ‘;’ at end of member declaration
./test.cpp:11:2: error: declaration of ‘typename std::enable_if<(! std::is_void<_Tp>::value), Ret>::type Test<Ret(Args ...)>::Ret’
./test.cpp:6:11: error:  shadows template parm ‘class Ret’
./test.cpp:11:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive]
./test.cpp:18:2: error: expected ‘;’ at end of member declaration
./test.cpp:18:2: error: declaration of ‘typename std::enable_if<std::is_void<_Tp>::value, Ret>::type Test<Ret(Args ...)>::Ret’
./test.cpp:6:11: error:  shadows template parm ‘class Ret’
./test.cpp:18:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive]
./test.cpp:18:6: error: ‘int Test<Ret(Args ...)>::operator()(Args ...)’ cannot be overloaded
./test.cpp:11:6: error: with ‘int Test<Ret(Args ...)>::operator()(Args ...)’
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...)’:
./test.cpp:22:2: warning: no return statement in function returning non-void [-Wreturn-type]
./test.cpp: In instantiation of ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’:
./test.cpp:28:10:   required from here
./test.cpp:13:7: error: variable or field ‘returnVal’ declared void
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’:
./test.cpp:15:2: warning: control reaches end of non-void function [-Wreturn-type]

私がただのばかで申し訳ありませんが、答えは明らかです。私はテンプレートにかなり慣れていないので、他のスレッド/質問で適切な答えを見つけることができませんでした.

4

2 に答える 2

7

あなたの説明から正確に明確でないことがいくつかあるので、最も一般的な答えから始めます。

テンプレートに動作を同じに保つ必要がある他の関数があり、その特定の関数の動作のみを再定義したい場合、最も簡単な答えはテンプレートを 2 つに分割し、継承を使用してそれらをマージすることです。この時点で、ベース テンプレートで部分的なテンプレートの特殊化を使用できます。

template <typename T, typename... Args>
struct tmpl_base {
   T operator()( Args... args ) {
       //generic
   }
};
template <typename... Args>
struct tmpl_base<void,Args...> {
   void operator()( Args... args ) {
   }
};

template <typename Ret, typename... Args>
class Test<Ret (Args...)> : tmp_base<Ret,Args...> {
   // do not declare/define operator(), maybe bring the definition into scope:
   using tmp_base<Ret,Args...>::operator();

   // Rest of the class

これがテンプレート内の唯一の関数である場合、部分的な特殊化は、継承を悪用する必要のない、はるかに単純なソリューションです。

于 2012-06-26T15:03:02.503 に答える
2

1 つの解決策は、クラス テンプレートを部分的に特殊化することです。

#include <iostream>
#include <type_traits>

template <typename T> class Test { };

template <typename Ret, typename... Args>
class Test<Ret (Args...)>
{
public:
    Ret operator()(Args...)
    {

        std::cout<<"non-void function"<<std::endl;
        Ret returnVal;
        return returnVal;

    }
};

template <typename... Args>
class Test<void (Args...)>
{
public:
    void operator()(Args...)
    {

        std::cout<<"void function"<<std::endl;
    }
};


int main(int argc, char * argv[])
{
    Test<void (char)> test;
    test('k');
}
于 2012-06-26T15:00:43.240 に答える