类模板特化中的decltype

时间:2011-02-21 02:32:28

标签: c++ c++11 template-specialization decltype

我正在尝试在模板类中使用decltype,如下所示:

#include <functional>
template <typename T>
class A
{
    typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;

    void f();
};

这很好,但现在我想添加一个明确的专业化:

template <>
class A<void>
{
    typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;

    void f();
};

这次g ++给出了一个错误:

test.cpp:14:33: error: incomplete type 'A<void>' used in nested name specifier

我做错了什么?我正在使用gcc 4.5。

编辑:如果我按照约翰内斯的建议将void f();的声明移到typedef之上,我会得到(稍微)不同的错误:

test.cpp:15:62: error: invalid use of incomplete type 'class A<void>'
test.cpp:13:1: error: declaration of 'class A<void>'
test.cpp:15:62: error:   initializing argument 2 of 'std::_Bind<typename std::_Maybe_wrap_member_pointer<_Tp>::type(_ArgTypes ...)> std::bind(_Functor, _ArgTypes ...) [with _Functor = void (A<void>::*)(), _ArgTypes = {A<void>}, typename std::_Maybe_wrap_member_pointer<_Tp>::type = std::_Mem_fn<void (A<void>::*)()>]'
test.cpp:15:62: error: invalid use of incomplete type 'class A<void>'
test.cpp:13:1: error: declaration of 'class A<void>'
test.cpp:15:62: error:   initializing argument 2 of 'std::_Bind<typename std::_Maybe_wrap_member_pointer<_Tp>::type(_ArgTypes ...)> std::bind(_Functor, _ArgTypes ...) [with _Functor = void (A<void>::*)(), _ArgTypes = {A<void>}, typename std::_Maybe_wrap_member_pointer<_Tp>::type = std::_Mem_fn<void (A<void>::*)()>]'

2 个答案:

答案 0 :(得分:4)

您的订单有误。尝试交换它

template <>
class A<void>
{    
    void f();
    typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};

在主模板中,名称A::f是依赖的,编译器将查找延迟到声明f的点(A::f不再真正依赖于C ++ 0x,因为A引用当前实例化并因此f指向当前实例化的成员,但由于当前规范存在漏洞(它与依赖基类有关),编译器延迟尽管如此)。在显式特化中,名称不依赖,并且立即执行查找,这是您在引用它之前需要声明f的原因。

修改:您错误地使用了std::bind。您给出的第二个参数是A<void>类型,它将被std::bind复制/移动到其创建的调用包装器对象中。这需要完整的类型A<void>

如果您只想传递对其中调用成员函数的A的引用,您可以传递declval<A*>() std::bind机制同样检测为神奇的第一个参数成员指针调用。

但在我看来,您希望调查std::function<>,而不是执行此std::binddecltype混乱。毕竟你有一个强大的工具集,但通过使用这个令人怀疑的decltype表达式,你抛弃了标准库给你的所有通用性,并限制自己使用那个单std::bind表达式。那不好。

答案 1 :(得分:2)

std :: bind需要A作为完整类型(请参阅Johannes的回答),因此您不能在此时使用它。作为解决方法,如果您将some_type封装为will compile

#include <functional>

template <typename T>
class A
{
  void f();
  struct some_type_helper
  {
    typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
  };
};

template <>
class A<void>
{
  void f();
  struct some_type_helper;
};

struct A<void>::some_type_helper
{
  typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};