4

基本クラスは次のとおりです。

#include <memory>

namespace cb{

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};
} // namespace cb

派生クラスはこれです:

namespace cb{
template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};
} // namespace cb

作成する関数:

namespace cb{
template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( typename FunctionCallback< R, Args... >::funccb cb )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}
} // namespace cb

そして例:

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = cb::MakeCallback( & Foo_1args );
}

私はこのエラーを受け取り続けます:

error: no matching function for call to ‘MakeCallback(bool (*)(const int&))’
error: unable to deduce ‘auto’ from ‘&lt;expression error>’

変更しようとしましたが、修正方法がわかりませんでした。

それで、何が問題なのですか?そして、この例を修正する方法は?

4

4 に答える 4

7

この問題は、より単純な例で意味をなす場合があります。ここで問題を特定してみてください。

template <typename T>
struct id { typedef T type; };

template <typename T>
void foo(typename id<T>::type x);

foo(5); // error

T問題は、コンパイラが何をすべきかを推測できないことです。どこにも直接使用されていません。明示的に提供する必要があります:foo<int>(5)、または他の方法で推測させます:

template <typename T>
void foo(typename id<T>::type x, T y);

foo(5, 7); // okay, T is int because 7 is int

これは理にかなっています。コンパイラはT、に提供されたのどれidid<T>::type一致するかをどのように判断できるでしょうか。専門分野がある可能性があり、可能であれば、とにかく全体がコストがかかるでしょう。


同様に、コンパイラが推測Rして使用できるものはありませんArgs。代わりに、これを行う必要があります。

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( R cb(Args...) )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb ));

    return p;
}

最後に、 Xeoが概説した、修正が必要な他の小さな問題があります。

于 2011-04-13T23:12:58.940 に答える
4

他の回答のコメントで私が述べたことを思い出すために:

  • まず、@ GManが言うように、あなたの議論はMakeCallback推論できませんでした。
  • 第二に、あなたの返品タイプMakeCallbackが間違っていました。typedefが存在しないためCallbackPtr、そうする必要があります。CallbackBasePtrこれにより、SFINAEが起動し、引数が修正された場合でも、関数を呼び出し可能な関数と見なさなくなります。
  • 第3に、FunctionCallbackコンストラクターはfunccb* ポインターを必要としていましたが、funccb既に(関数)ポインターであるため、たとえば、ポインターから関数ポインターを渡す必要があります。new FunctionCallback(&cb)
于 2011-04-13T23:31:22.353 に答える
4

一部のtype-oおよび特殊なMakeCallbackを修正して、関数ポインターを受け入れるようにしました。GManが言ったように、MakeCallbackへのテンプレート引数は推論できないコンテキストにあります。

#include <memory>

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};

template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb  cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackPtr
    MakeCallback( R (*cb)(Args...)  )
{
    typename CallbackBase< R, Args... >::CallbackPtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = MakeCallback( & Foo_1args );
}

アップデート:

C ++標準では、14.8.2.5 [temp.deduct.type]の段落5〜6で、推論されないコンテキストが定義されています。完全に理解しているとは言えない箇条書きがあります。私にとってのマーカーは次のとおりです。

テンプレートパラメータの後に「::」が表示されている場合は常に、そのテンプレートパラメータは推定されないコンテキストにあります。つまり、呼び出しサイトで明示的に指定する必要があります。

于 2011-04-13T23:17:53.000 に答える
4

再発明するよりも、使用するほうがよいでしょう<functional>。また、コンパイラの実装から直接コピーする方がよいでしょう。

一般に、使用するテンプレート パラメーターを少なくすることも良いことです。

しかし、これらの問題を解決することは常に魅力的です... 私が何をしているのかは知っていますが、今はそれを直接見ていません。

単純なファンクターへのコードCallは、さまざまな種類のファンクターに特化されていないため、一般的なテンプレート ケースにする必要があります。

一般的なテンプレートを微調整するには、特性クラスが最適です。

template< typename F, typename = void >
struct callback_traits {
    typedef F const &local_type; // fallback case: only keep a reference
};

template< typename F >
struct callback_traits< F,
    typename std::enable_if< // GCC 4.4 missing is_copy_constructible:
        std::is_constructible< F, F const& >::value
    >::type > {
    typedef F local_type; // better to keep a copy as this is a callback
};

template< typename F >
struct Callback {
    typedef typename callback_traits< F >::local_type local_type;
    local_type fn;

    Callback( local_type const &fn_in ) : fn( fn_in ) {}
    template< typename ... Args >
    typename std::result_of< local_type( Args ... ) >::type
    Call( Args ... a )
        { return fn( a ... ); }
};
于 2011-04-13T23:35:21.003 に答える