decltype in class template specialization
I am trying to use declt开发者_如何学JAVAype inside a template class as follows:
#include <functional>
template <typename T>
class A
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
void f();
};
That works fine, but now I'd like to add an explicit specialization:
template <>
class A<void>
{
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
void f();
};
This time g++ gives an error:
test.cpp:14:33: error: incomplete type 'A<void>' used in nested name specifier
What am I doing wrong? I am using gcc 4.5.
EDIT: If I move the declaration of void f();
to above the typedef, as suggested by Johannes, I get (slightly) different errors:
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>::*)()>]'
Your order is wrong. Try exchanging it
template <>
class A<void>
{
void f();
typedef decltype(std::bind(&A::f, std::declval<A>())) some_type;
};
In the primary template, the name A::f
was dependent and the compiler delayed lookup to a point where f
was declared (A::f
is not really dependent in C++0x anymore, since A
refers to the current instantiation and therefor f
to a member of the current instantiation, but as there is a loophole in the current specification (it has to do with dependent base classes), the compiler delayed the lookup nontheless). In the explicit specialization, the name is not dependent and lookup is done immediately, which is the reason you need to declare f
before referring to it.
Edit: You are wrongly using std::bind
. The second argument you give is of type A<void>
, which will be copied/moved by std::bind
into its created call wrapper object. This requires a complete type A<void>
.
If you want to merely pass a reference to A
on which the member function is called, you can pass a declval<A*>()
, which the std::bind
mechanism equally detects as magical first argument to a member pointer invocation.
But it seems to me you want to look into std::function<>
, instead of doing this std::bind
and decltype
mess. After all you have a powerful toolset given, but by using this doubtful decltype
expression, you throw away all the genericity the Standard library gives you and restrict yourself to use of that single std::bind
expression. That's no good.
std::bind requires A as a complete type (see answer by Johannes) and therefore you cannot use it at this point. As a workaround, if you encapsulate the some_type this 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;
};
精彩评论