为什么is_class <t>不在此代码段中工作?</t>

时间:2013-03-10 12:11:56

标签: 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);
}

这是在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类似的方案无需工作。

1 个答案:

答案 0 :(得分:7)

std::is_class<>特性工作正常,编译器几乎告诉你问题是什么:

  

test.cpp:14:17:注意:从Foo* constconst Foo*&

的参数1没有已知的转换

您正在以这种方式调用您的功能模板:

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);
//                             ^^^^^^^^^
}