确保派生类实现静态方法

时间:2014-04-29 14:14:37

标签: c++ c++11 typetraits crtp static-assert

我想确保派生类实现特定的静态方法。我认为应该可以使用static_assertstd::is_samedecltypeCRTP并使用SFINAE来实现这一目标。但是,到目前为止我发现的similar code非常复杂,似乎我还没有完全理解它使我无法将其用于我的需要。

到目前为止我尝试的是

template <class T>
class Base 
{
    static_assert(std::is_same<decltype(T::foo(1)), int>::value, "ERROR STRING");
};

class Derived : public Base <Derived>
{
public:
    static int foo(int i) { return 42; };
};

然而,它没有编译告诉我,即使方法正确实现,Derived也没有名为foo的元素。此外,在static_assert内的表达式中为foo提供实际参数会感觉不对。

搜索SO显示了一个类似的问题,最终导致我this piece of code,检查类型是否有方法begin()和end()返回迭代器。所以我试着根据我的需要采用这个代码。

template <class T>
class Base 
{
    template<typename C>
    static char(&g(typename std::enable_if<std::is_same<decltype(static_cast<int(C::*)(int)>(&C::foo)), int(C::*)(int)>::value, void>::type*))[1];

    template<typename C>
    static char(&g(...))[2];

    static_assert(sizeof(g<T>(0)) == 1, "ERROR STRING");
};

但是这段代码没有编译,因为断言会触发。

所以我的问题是

  1. 为什么编译器在我的第一个例子中找不到Derived :: foo?
  2. 示例代码中的typename C::const_iterator(C::*)() const究竟是什么意思?是不是一个const函数返回C :: const_iterator并且没有参数? C::*究竟是什么意思?那么为什么int(C::*)(int)在我的情况下会出错?
  3. 如何正确解决我的问题?
  4. 我正在使用MSVC 12,但如果可能,代码应该是可移植的。

1 个答案:

答案 0 :(得分:12)

使用CRTP时这是一个常见问题:Base<Derived>会在Derived的基础列表中遇到Derived时被实例化,此时static_assert是还没有一个完整的类型,因为其声明的其余部分尚未被解析。有各种解决方法。对于Derived,您需要延迟断言的实例化,直到Base完成。一种方法是将断言放在template <class T> class Base { public: ~Base() { static_assert(std::is_same<decltype(T::foo(1)), int>::value, "ERROR STRING"); } }; class Derived : public Base<Derived> { public: static int foo(int) { return 42; }; }; 的成员函数中,你知道必须将其实例化 - 析构函数总是一个不错的选择(Live at Coliru):

C::*

解决问题#2:C是&#34;指向类int(*)(int)成员的指针的语法。&#34;所以int是&#34;指向函数的指针,它接受一个int参数并返回int(C::*)(int)&#34;,而C类似于&#34;指向成员的指针int获取单个int参数并返回typename C::const_iterator(C::*)() const 的函数。&#34;怪物

C

将转换为&#34;指向C::const_iterator的常量成员函数的指针,不带参数并返回typename&#34;当然,C::const_iterator必须表明依赖名称{{1}}是一种类型。