确保派生类实现静态方法

时间:2023-01-15 20:39:30

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.

我希望确保派生类实现特定的静态方法。我认为应该使用static_assert、std::is_same、decltype、CRTP和可能使用SFINAE来实现。然而,我目前发现的类似的代码相当复杂,我似乎还没有完全理解它使我无法将它应用到我的需求中。

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.

但是,它并没有编译告诉我,即使方法被正确实现,派生没有一个名为foo的元素。此外,在static_assert中的表达式中为foo提供实际参数,感觉是错误的。

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.

搜索如此显示了一个类似的问题,它最终引导我找到这段代码,其中检查类型是否有方法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");
};

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. 为什么编译器在我的第一个例子中找不到派生的::foo ?
  3. 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?
  4. typename C::const_iterator(C:::*)() const在示例代码中的确切含义是什么?它不是一个返回C: const_iterator且不接受参数的const函数吗?C::*是什么意思?那么为什么int(C:::*)(int)在我的例子中是错的呢?
  5. How to correctly solve my problem?
  6. 如何正确地解决我的问题?

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

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

1 个解决方案

#1


12  

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):

在使用CRTP: Base <派生> 时,这是一个常见的问题。在派生的基列表中遇到这个问题时,派生的时间还不是一个完整的类型,因为其声明的其余部分还没有被解析。有各种各样的解决方法。对于static_assert,您需要延迟断言的实例化,直到派生完成。一种方法是将断言放在基的成员函数中,您知道必须实例化它——析构函数总是一个很好的选择(在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

寻址问题2:C::*是“指向类C成员的指针”的语法。因此int(*)(int)是“取一个int参数并返回int的函数指针”,而int(C:::*)(int)类似地是“取一个int参数返回int的C的成员函数指针”。的怪物

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.

将转换为“指向C的常量成员函数的指针,该函数不接受任何参数并返回C::const_iterator”,当然,需要使用typename来表示依赖名称C::const_iterator是一种类型。

#1


12  

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):

在使用CRTP: Base <派生> 时,这是一个常见的问题。在派生的基列表中遇到这个问题时,派生的时间还不是一个完整的类型,因为其声明的其余部分还没有被解析。有各种各样的解决方法。对于static_assert,您需要延迟断言的实例化,直到派生完成。一种方法是将断言放在基的成员函数中,您知道必须实例化它——析构函数总是一个很好的选择(在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

寻址问题2:C::*是“指向类C成员的指针”的语法。因此int(*)(int)是“取一个int参数并返回int的函数指针”,而int(C:::*)(int)类似地是“取一个int参数返回int的C的成员函数指针”。的怪物

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.

将转换为“指向C的常量成员函数的指针,该函数不接受任何参数并返回C::const_iterator”,当然,需要使用typename来表示依赖名称C::const_iterator是一种类型。