尝试在下面的片段中使用 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);
}
这是在带有 gcc -std=c++11 test.cpp 命令行的 cygwin 上使用 gcc v4.7.2。
输出是:
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);
}
怎么了?
谢谢,
PS 与 is_enum 类似的方案没有任何问题。
最佳答案
std::is_class<>
trait 工作正常,编译器几乎可以告诉您问题出在哪里:
test.cpp:14:17: note: no known conversion for argument 1 from
Foo* const
toconst 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);
// ^^^^^^^^^
}
关于c++ - 为什么 is_class<T> 在此代码段中不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15322065/