这个可变参数模板示例有什么问题?

发布于 2024-11-01 03:54:28 字数 1649 浏览 3 评论 0原文

基类是:

#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 ‘<expression error>’

我尝试更改它,但我不知道如何修复。

那么,到底出了什么问题呢?以及如何解决这个例子?

The base class is :

#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

Derived class is this :

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

Function to create :

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

And the example :

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

I keep getting this error :

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

I tried to change it, but I couldn't figure out how to fix.

So, what is wrong? And how to fix this example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

套路撩心 2024-11-08 03:54:28

通过一个更简单的例子,这个问题可能会有意义。尝试在这里找出问题:

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

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

foo(5); // error

问题是编译器无法推断出 T 应该是什么;它没有在任何地方直接使用。您必须显式提供它: foo(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< /code> 提供给 id 会导致 id::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 已概述

The problem might make sense with a simpler example. Try identifying the problem here:

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

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

foo(5); // error

The problem is that the compiler cannot deduce what T should be; it's not directly used anywhere. You'd have to explicitly provide it: foo<int>(5), or let it deduce it in some other way:

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

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

This makes sense: how could the compiler figure out which T's, supplied to id, result in id<T>::type matching? There could be specializations, and the entire thing would be costly anyway, if possible.


Likewise, there's nothing the compiler has available to deduce R and Args. Instead, you should do this:

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;
}

Finally, you have other minor issues that need fixing, which Xeo has outlined.

殤城〤 2024-11-08 03:54:28

回想一下我在其他答案的评论中提到的内容:

  • 首先,正如 @GMan 所说,您的 MakeCallback 参数是不可推导的。
  • 其次,您的 MakeCallback 返回类型是错误的。它应该是 CallbackPtr,因为 CallbackBasePtr typedef 不存在。这导致 SFINAE 即使参数已修复,也不会将您的函数视为可能调用的函数。
  • 第三,您的 FunctionCallback 构造函数需要一个 funccb* 指针 指针,而 funccb 已经是一个(函数)指针,所以你必须传递一个函数指针的指针,例如。 new FunctionCallback(&cb)

To recollect what I mentioned in the comments of the other answers:

  • First, as @GMan says, your argument of MakeCallback was non-deducible.
  • Second, your return type of MakeCallback was wrong. It should be CallbackPtr, as a CallbackBasePtr typedef doesn't exist. This lead to SFINAE kicking in and not considering your function as a possible function to call even when the argument was fixed.
  • Third, your FunctionCallback constructor wanted a funccb* pointer, while funccb already is a (function-)pointer, so you would have to pass a pointer-to-function-pointer, eg. new FunctionCallback(&cb)
权谋诡计 2024-11-08 03:54:28

使用 比重新发明它更好……直接从编译器的实现中复制也更好。

一般来说,使用较少的模板参数也是一件好事。

但是,解决这些问题总是很诱人……所以,知道我在做什么,但现在不直接看它,这就是我处理它的方法。

简单地调用函子的代码不会专门针对不同类型的函子,因此它应该在一般模板情况下。

要对通用模板进行细微调整,特征类最适合。

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 ... ); }
};

It's better to use <functional> than to reinvent it… It's also better to copy directly from your compiler's implementation.

Generally, using fewer template parameters is a good thing, too.

But, it's always tempting to solve these problems… so, knowing what I do, but not looking directly at that right now, here is how I'd handle it.

The code to simply Call a functor will not be specialized for the different kinds of functors, so it should be in the general template case.

To make minor adjustments to the general template, a traits class serves best.

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 ... ); }
};
秋意浓 2024-11-08 03:54:28

修复了一些 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 段中定义了非推导上下文。那里有一个我赢得的项目符号列表不声称完全理解。我的标记是:

任何时候你在你的后面看到“::”
template参数,那个模板
参数是非推导的
上下文,这意味着它必须是
在调用站点明确指定。

Fixed some type-o's and specialized MakeCallback to accept function pointers. As GMan said, your template arguments to MakeCallback are in a non-deducible context.

#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 );
}

Update:

The C++ standard defines non-deduced context in 14.8.2.5 [temp.deduct.type], paragraphs 5 - 6. There is a bulleted list there which I won't claim to fully understand. The marker for me is:

Any time you see "::" after your
template parameter, that template
parameter is in a non-deduced
context
, meaning it must be
explicitly specified at the call site.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文