成员函数检查:用c++ 11特性实现编译时检查

Member Function checking: Implement compilation-time checkings with C++11 features

本文关键字:检查 实现 编译 c++ 函数 成员      更新时间:2023-10-16

我读到c++ 11有足够的静态检查(编译时),以实现c++ 11中应该是概念检查的很大一部分(已删除)。(我在最近一个关于概念删除的问题的评论中看到了这一点…

下面的c++ 03代码只检查类中成员函数的存在(我的模板类想要在其上工作)。下面是要搜索的4个成员函数,我总是使用相同的模式:

  • 一个typedef定义函数原型的typedef
  • 对static_cast的调用,如果typename TExtension没有定义这样的成员函数,或者如果它具有不同的原型,则会中断编译

代码如下:

template <typename TExtension>
class
{
...
    void checkTemplateConcept()
    {
        typedef unsigned long (TExtension::*memberfunctionRequestedId)();
        static_cast<memberfunctionRequestedId>(&TExtension::getRequestId);
        typedef eDirection (TExtension::*memberfunctionDirection)();
        static_cast<memberfunctionDirection>(&TExtension::getDirection);
        typedef eDriveWay (TExtension::*memberfunctionDriveWay)();
        static_cast<memberfunctionDriveWay>(&TExtension::getDriveWay);
        typedef unsigned long (TExtension::*memberfunctionCycleId)();
        static_cast<memberfunctionCycleId>(&TExtension::getCycleId);
    }
}

这是在我的代码的某些部分,但它完全基于 c++ 03。我很乐意重写它,使用那些新的c++ 11特性…这里应该用什么呢?

在c++ 11中,您可以让编译器打印static_assert的正确错误消息:

typedef unsigned long (TExtension::*required_type)();
typedef decltype(&TExtension::getRequestId) actual_type;
static_assert(std::is_same<required_type, actual_type>::value,
     "The type of getRequestId must be unsigned long (C::*)()");

现在,如果成员函数的类型不匹配,编译器将打印这条有用的信息:

"The type of getRequestId must be unsigned long (C::*)()"

如果你愿意,你可以让它更具描述性。: -)

是的,在c++ 11中,SFINAE被扩展到表达式,所以你可以定义一个像is_t_extension这样的trait来检测成员函数的存在,像这样:

template<class... T>
struct holder
{
    typedef void type;
};
template<class T, class=void>
struct is_t_extension
: std::false_type
{};
template<class T, class=void>
struct is_t_extension<T, typename holder<
    decltype(std::declval<T>().getRequestId),
    decltype(std::declval<T>().getDirection),
    decltype(std::declval<T>().getDriveWay),
    decltype(std::declval<T>().getCycleId)
>::type>
: std::true_type
{};

现在这只是检查它们的存在。通过一些工作,您可以扩展上面的代码来检测有效的签名,或者您可以使用Tick库来编写trait,这样看起来干净多了:

TICK_TRAIT(is_t_extenstion)
{
    template<class T>
    auto requires_(T&& x) -> TICK_VALID(
        returns<unsigned long>(x.getRequestId),
        returns<eDirection>(x.getDirection),
        returns<eDriveWay>(x.getDriveWay),
        returns<unsigned long>(x.getCycleId)
    );
};

然后在你的类你可以使用static_assert报告错误:

template <typename TExtension>
class foo
{
    static_assert(is_t_extension<TExtension>(), "Not a TExtension");
};

或者,如果您想允许专门化,您可以使用TICK_CLASS_REQUIRES:

template <typename TExtension, class=void>
class foo;
template <typename TExtension>
class foo<TExtension, TICK_CLASS_REQUIRES(is_t_extension<TExtension>())>
{
    ...
};