CRTP - 从基类检查派生的符合要求

时间:2016-04-16 23:56:26

标签: c++ c++14 crtp

奇怪的重复出现的模板模式可用于实现一种静态多态性。例如:

#include <iostream>

template<
 class Derived
>
struct Base
{
    static void print ( )
    {
        std::cout << Derived::number_to_print << '\n';
    }
};

struct Derived final :
    Base<
     Derived
    >
{
    static constexpr unsigned int number_to_print = 27;
};

int main ( )
{
    Derived::print();
}

此操作符合预期并打印27

现在,我想向基类添加检查以断言派生类满足某些要求。在上面给出的例子中,这种检查可以是:

#include <iostream>
#include <type_traits>

template<
 class Derived
>
struct Base
{
    // --- Checks begin
    static_assert( std::is_same<
         decltype(Derived::number_to_print),
         unsigned int
        >::value,
        "static member `number_to_print' should be of type `unsigned int'" );
    // --- Checks end

    static void print ( )
    {
        std::cout << Derived::number_to_print << '\n';
    }
};

struct Derived final :
    Base<
     Derived
    >
{
    static constexpr unsigned int number_to_print = 27;
};

int main ( )
{
    Derived::print();
}

这不起作用,因为在Base<Derived>被实例化的时候,Derived只是前向声明的,即它不完整,并且没有任何内容但它已经超越了它的结构这一事实。

我一直在摸不着头脑,因为我认为这些检查可能对基层用户有所帮助,但还没有找到任何办法。

有可能吗?如果有,怎么样?

3 个答案:

答案 0 :(得分:4)

作为noted by Kerrek SB,您可以将静态断言移动到成员函数的主体中。

当您提供非static成员函数的功能时,您可以让断言函数体成为构造函数的主体,而不是当前的static成员函数。

E.g。

#include <iostream>
#include <type_traits>
using namespace std;

#define STATIC_ASSERT( e ) static_assert( e, #e " // <- is required" )

template< class A, class B >
constexpr auto is_same_() -> bool { return std::is_same<A, B>::value; }

template< class Derived >
struct Base
{
    Base()
    {
        STATIC_ASSERT((
            is_same_< remove_cv_t< decltype( Derived::number_to_print ) >, int >()
            ));
    }

    void print() const
    {
        std::cout << Derived::number_to_print << '\n';
    }
};

struct Derived final
    : Base< Derived >
{
    static constexpr int number_to_print = 27;
};

auto main()
    -> int
{
    Derived::Base().print();
}

答案 1 :(得分:3)

作为一个肮脏的技巧,你可以将静态断言移动到成员函数的主体中。由于成员函数定义在类定义之后立即有效地出现,因此Derived的类型在成员函数体内完成。

请注意,类模板的成员函数本身就是函数模板,因此只有在使用时才会实例化(或者如果明确地实例化类模板)。

答案 2 :(得分:1)

作为替代方法(其他答案确实相当不错),您可以使用私有方法并依赖 sfinae

#include <iostream>
#include <type_traits>

template<class Derived>
struct Base {
    static void print ( ) {
        print<Derived>();
    }

private:
    template<class D>
    static
    std::enable_if_t<std::is_same<std::remove_cv_t<decltype(D::N)>, unsigned int>::value>
    print() {
       std::cout << D::N << '\n';
    }
};

struct Derived final: Base<Derived> {
    static constexpr unsigned int N = 27;
};

int main ( ) {
    Derived::print();
}

这样,只有在您实际使用print时才会出现错误。