c++ - 为什么 is_class<T> 在此代码段中不起作用?

标签 c++ templates c++11 typetraits

尝试在下面的片段中使用 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 to 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);
//                             ^^^^^^^^^
}

关于c++ - 为什么 is_class<T> 在此代码段中不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15322065/

相关文章:

c++ - 什么是 CTime(有没有)32 位模拟?

c++ - 命名空间中的模板函数导致错误

c++ - 如何找到 C++ 标准库的实现?

php - drupal 分隔模板代码页眉和页脚包含

java - 使用反射生成的 Java 源代码

c++ - 如何在不导致复制的情况下将 char 数据附加到 std::vector

c++ - 如何编写 makefile 来处理头文件中的更改

c++ - 使 priority_queue 按最小值排序

c++ - 赋值子表达式的求值顺序

c++ - 使用模板转换特定类型的参数