如何重写它以使其符合C ++标准

时间:2013-03-04 00:28:45

标签: c++ templates template-specialization

以下代码片段演示了我想要实现的内容,即创建两个模板特化(嗯,这里是主模板和特化),一个用于非const成员函数,一个用于const成员函数:

// instantiate for non-const member functions
template <typename C, void(C::*F)()>
struct A {};

// instantiate for const member functions
template <typename C, void(C::*F)() const>
struct A<C const, F> {};

struct foo
{
    void bar() const {}
    typedef A<foo const, &foo::bar> bar_type;

    void baz() {}
    typedef A<foo, &foo::baz> baz_type;
};

虽然这段代码使用gcc 4.7,Intel 13.0和MSVC 2012编译得很好,但它无法使用Clang 3.3或Comeau 4.3.10.1进行编译。我相信Clang实际上是对的。

如何重写此代码以使其符合标准(即使用Clang进行编译)?

以下是编译错误:

test_6.cpp:22:26: error: non-type template argument of type 'void (foo::*)() const' cannot be converted to a value of type 'void (const foo::*)()'
    typedef A<foo const, &foo::bar> bar_type;
                         ^~~~~~~~~
test_6.cpp:7:33: note: template parameter is declared here
template <typename C, void (C::*F)()>
                                ^

3 个答案:

答案 0 :(得分:3)

这不是(部分)专业化的工作方式。当您在特化中提供参数时,它必须匹配相应参数的类型。

您的主模板有一个指向非const -member-function (PTNCMF)参数的指针,期望这种模板参数。但是,您的部分特化会将指针指向 const -member-function (PTCMF)作为参数传递,从而产生不匹配。 PTCMF不能转换为PTNCMF,因此部分特化无效。

解决问题的方法很多。您需要从实际参数中分离参数的类型。一种方法如下,简单断言const类类型只与PTCMF匹配。

#include <type_traits>

template<class Fty>
struct is_ptcmf : std::false_type{};

template<class C, class R, class... Args>
struct is_ptcmf<R (C::*)(Args...) const> : std::true_type{};

template<class C, class Fty, Fty F>
struct A{
  static_assert(std::is_const<C>() == is_ptcmf<Fty>(), "Must pair const with const.");
};

Live example.

然后使用A<foo, decltype(&foo::bar), &foo::bar>。如果您认为存在一些冗余,我同意,但there is no nice way yet to get rid of it

答案 1 :(得分:3)

如果使成员函数键入模板参数,则可以为不同的成员函数类型专门化模板:

template <typename C, typename F, F>
struct A;  // undefined

template <typename C, void(C::*f)()>
struct A<C, void(C::*)(), f> {};

template <typename C, void(C::*f)() const>
struct A<C const, void(C::*)() const, f> {};

struct foo
{
    void bar() const {}
    typedef A<foo const, decltype(&foo::bar), &foo::bar> bar_type;

    void baz() {}
    typedef A<foo, decltype(&foo::baz), &foo::baz> baz_type;
};

答案 2 :(得分:1)

仅供记录,以下是我解决的方法:

template <typename F, F ptr>
struct A;  // undefined

template <typename C, void (C::*F)()>
struct A<void (C::*)(), F> {};

template <typename C, void (C::*F)() const>
struct A<void (C::*)() const, F> {};

struct foo
{
    void bar() const {}
    typedef A<decltype(&foo::bar), &foo::bar> bar_type;

    void baz() {}
    typedef A<decltype(&foo::baz), &foo::baz> baz_type;
};

感谢大家的见解!