CRTP - 从基础 class 检查派生的是否满足要求
CRTP - Checking from the base class that the derived one meets requirements
奇怪的重复出现的模板模式可用于实现一种静态多态性。例如:
#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
。
现在,我想向基础 class 添加检查以断言派生的 class 满足某些要求。在上面给出的示例中,此类检查可以是:
#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
只是前向声明的,也就是说,它是不完整的,除了事实上它是一个结构。
我一直在摸不着头脑,因为我认为这些检查可能会对基地的用户有所帮助 class,但还没有找到任何方法来做到这一点。
这可能吗?如果可能,怎么做?
作为肮脏的把戏,您可以将静态断言移到成员函数的主体中。由于成员函数定义在 class 定义后 有效地立即出现,因此 Derived
的类型在成员函数体内是完整的。
请注意 class 模板的成员函数本身就是函数模板,因此它们仅在使用时被实例化(或者如果 class 模板被显式实例化)。
作为 您可以将静态断言移动到成员函数的主体中。
并且当您将功能作为非static
成员函数而不是当前static
成员函数提供时,您可以让断言函数主体成为构造函数的主体。
例如
#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();
}
作为替代方法(其他答案确实不错),您可以使用私有方法并依赖 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
.
时才会出现错误
奇怪的重复出现的模板模式可用于实现一种静态多态性。例如:
#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
。
现在,我想向基础 class 添加检查以断言派生的 class 满足某些要求。在上面给出的示例中,此类检查可以是:
#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
只是前向声明的,也就是说,它是不完整的,除了事实上它是一个结构。
我一直在摸不着头脑,因为我认为这些检查可能会对基地的用户有所帮助 class,但还没有找到任何方法来做到这一点。
这可能吗?如果可能,怎么做?
作为肮脏的把戏,您可以将静态断言移到成员函数的主体中。由于成员函数定义在 class 定义后 有效地立即出现,因此 Derived
的类型在成员函数体内是完整的。
请注意 class 模板的成员函数本身就是函数模板,因此它们仅在使用时被实例化(或者如果 class 模板被显式实例化)。
作为
并且当您将功能作为非static
成员函数而不是当前static
成员函数提供时,您可以让断言函数主体成为构造函数的主体。
例如
#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();
}
作为替代方法(其他答案确实不错),您可以使用私有方法并依赖 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
.