2

尝试在以下部分中使用 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);
}

怎么了?

谢谢,D

PS 与 is_enum 类似的方案可以在没有问题的情况下工作。

4

1 回答 1

8

std::is_class<>特征工作正常,编译器几乎可以告诉您问题所在:

Foo* consttest.cpp:14:17:注意:参数 1 从到的未知转换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是对指向 a 的指针的常量引用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则将推断为 be 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) { }
//                     ^^^^^^^^^^

更改后,您可以在此处看到整个编译过程。

更新:

作为评论的后续,事实证明您一定希望static成员函数xxxU_push()接受指向的指针const

 static void xxxU_push (Foo const*& value) { }

在这种情况下,您必须做出决定:您不能将 a 传递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);
//                             ^^^^^^^^^
}
于 2013-03-10T12:30:42.977 回答