确保派生类实现静态方法 [英] Ensure derived class implements static method

查看:127
本文介绍了确保派生类实现静态方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想确保一个派生类实现一个特定的静态方法。我认为这样做应该可以使用 static_assert std :: is_same decltype CRTP ,并且可以使用 SFINAE 。但是,类似代码我发现到目前为止相当复杂,它似乎我还没有



到目前为止,我尝试的是

  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揭示了一个类似的问题,最终导致了这段代码,其中检查类型有方法begin()和end()返回迭代器。因此,我试图根据我的需要采用这个代码。

 模板< 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. 如何正确地解决我的问题?

我使用MSVC 12,但如果可能,代码应该是可移植的。

解决方案

这是使用CRTP时的一个常见问题: Base< Derived> code> Derived 的列表,此时派生不是一个完整的类型,因为其声明的其余部分hasn'已经解析了。有各种解决方法。对于 static_assert ,您需要延迟断言的实例化,直到 Derived 完成。一种方法是将断言放在 Base 的成员函数中,你知道必须实例化 - 析构函数总是一个好的选择( Live at Coliru ):

  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; };
};

解决问题#2: C :: * C 成员的指针的语法。因此 int(*)(int)是指向函数的指针,使用单个 int 参数并返回 int int(C :: *)(int)类似地 C 获取单个 int 参数并返回 int 。怪物

  typename C :: const_iterator(C :: *)()const 

将转换为指向 C 的常量成员函数的指针,不带参数并返回 c:C $ c> c :: const_iterator 其中当然需要 typename 表示依赖名 C: :const_iterator 是一个类型。


I want to ensure, that a derived class implements a specific static method. I think doing so should be possible using static_assert, std::is_same, decltype, CRTP and maybe making use of SFINAE. However, similar code I found so far is quite complex and it seems I do not yet fully understand it making me unable to adopt it to my needs.

What I tried so far is this

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; };
};

However, it does not compile telling me, that Derived does no have an element named foo even if the method is correctly implemented. Furthermore providing actual parameters for foo in the expression inside static_assert feels wrong.

Searching SO revealed a similar question which finally lead me to this piece of code where it is checked that a type has methods begin() and end() returning iterators. So I tried to adopt this code to my needs.

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");
};

But this code does not compile because the assertion fires.

So my questions are

  1. Why does the compiler cannot find Derived::foo in my first example?
  2. What exactly does typename C::const_iterator(C::*)() const in the example code mean? Isn't it a const function returning C::const_iterator and taking no arguments? What exactly does C::* mean? So why is int(C::*)(int) then wrong in my case?
  3. How to correctly solve my problem?

I'am using MSVC 12 but if possible the code should be portable.

解决方案

This is a common problem when using CRTP: Base<Derived> is instantiated at the point where it is encountered in Derived's list of bases, at which time Derived is not yet a complete type since the rest of its declaration hasn't been parsed yet. There are various workarounds. For static_assert, you need to delay instantiation of the assertion until Derived is complete. One way to do so is to put the assertion in a member function of Base that you know must be instantiated - the destructor is always a good choice (Live at Coliru):

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; };
};

Addressing question #2: C::* is the syntax for "pointer to member of class C." So int(*)(int) is "pointer to function taking a single int parameter and returning int", and int(C::*)(int) is analogously "pointer to member function of C taking a single int parameter and returning int." The monstrosity

typename C::const_iterator(C::*)() const

would translate to "pointer to constant member function of C taking no parameters and returning C::const_iterator" where of course the typename is necessary to indicate that the dependent name C::const_iterator is a type.

这篇关于确保派生类实现静态方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆