尝试在以下段中使用is_class(从较大的段中剥离),但它似乎不起作用。怎么了?
#include <type_traits>
template<typename U, typename = void>
struct xxxU_Impl
{
static void xxxU_push (const U& value);
};
template<typename U> void xxxU_push (const U& value) { xxxU_Impl<U>::xxxU_push (value); }
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (const U *& value) { }
};
class Foo
{
public:
int mFoo;
};
int main () {
Foo * pFoo = new Foo;
xxxU_push<Foo *>(pFoo);
}
这是在cygwin上的gcc v4.7.2和gcc -std = c ++ 11 test.cpp命令行。
输出结果为:
test.cpp: In instantiation of 'void xxxU_push(const U&) [with U = Foo*]':
test.cpp:26:23: required from here
test.cpp:9:63: error: no matching function for call to 'xxxU_Impl<Foo*, void>::xxxU_push(Foo* const&)'
test.cpp:9:63: note: candidate is:
test.cpp:14:17: note: static void xxxU_Impl<U*, typename std::enable_if<std::is_class<_Tp>::value>::type>::xxxU_push(const U*&) [with U = Foo]
test.cpp:14:17: note: no known conversion for argument 1 from 'Foo* const' to 'const Foo*&'
**
:** 这是带有注释的修改后的代码,恕我直言,这些注释表明类型现在是相同的。不过,我收到了编译错误。
#include <type_traits>
template<typename U, typename = void>
struct xxxU_Impl
{
static void xxxU_push (const U & value); // U=Foo*: const Foo* & value ==
// Foo const * & value
};
template<typename U> void xxxU_push (const U & value) // U=Foo*: const Foo* & value ==
// Foo const * & value
{ xxxU_Impl<U>::xxxU_push (value); }
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (U const * & value) { } // U=Foo: Foo const * & value
};
class Foo
{
public:
int mFoo;
};
int main () {
Foo* pFoo = new Foo;
xxxU_push<Foo*>(pFoo);
}
怎么了?
THX, d
PS与is_enum类似的方案无需工作。
答案 0 :(得分:7)
std::is_class<>
特性工作正常,编译器几乎告诉你问题是什么:
test.cpp:14:17:注意:从
的参数1没有已知的转换Foo* const
到const Foo*&
您正在以这种方式调用您的功能模板:
xxxU_push<Foo *>(pFoo);
这意味着U
将是Foo*
。现在函数签名:
template<typename U>
void xxxU_push (const U& value)
等同于:
template<typename U>
void xxxU_push (U const& value)
在为Foo*
替换U
之后,你得到了这个:
void xxxU_push (Foo* const& value)
因此,value
是指向Foo
的指针的常量引用。在函数内部,您可以通过以下方式实例化类模板:
xxxU_Impl<U>::xxxU_push (value);
当再次用Foo*
代替U
时:
xxxU_Impl<Foo*>::xxxU_push (value);
现在,您的类模板特化以这种方式定义:
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (const U *& value) { }
};
如果您使用Foo*
作为模板参数进行实例化,则U
将被推断为Foo
,其中 是类类型。因此,您的类模板可以在没有失败的情况下实例化(不确定这是您想要的,但这肯定会发生什么),特别是xxxU_push()
函数以这种方式实例化:
static void xxxU_push (const Foo *& value) { }
这相当于:
static void xxxU_push (Foo const*& value) { }
你能看出区别吗?在调用站点上,你有一个非常量指针的常量引用,这里你有一个常量指针的非常量引用!这两种类型不同,编译器抱怨它无法转换参数。
您可以修改错误,例如,更改xxxU_push()
的签名,如下所示:
static void xxxU_push (U * const& value) { }
// ^^^^^^^^^^
完成此更改后,您可以看到整个事情正在编译here。
<强>更新强>
作为评论的后续跟进,结果证明您必须希望static
成员函数xxxU_push()
接受指向const
的指针:
static void xxxU_push (Foo const*& value) { }
在这种情况下,您必须做出决定:您不能将Foo*
传递给接受非常量引用到const
指针的函数。但是,您可以删除引用:
static void xxxU_push (Foo const* value) { }
这是第一种使程序编译的可能性。第二种可能是更改调用站点,以便它提供指向const
:
int main () {
Foo * pFoo = new Foo;
Foo const* pConstFoo = pFoo;
xxxU_Impl<Foo*>::xxxU_push(pConstFoo);
// ^^^^^^^^^
}