确保派生 class 在保持默认移动/移动分配的同时实现静态方法

Ensure derived class implements static method while maintaining default move / move assign

我想确保一些派生的 classes 实现了一个静态方法并发现了这个 SO 问题:Ensure derived class implements static method 最佳答案使用 CRTP 来解决 static_assert 的问题在基础 class 析构函数中确保模板参数类型实现 static int foo(int).

但是,正如对答案的评论所指出的“这个答案有一个小问题:写出析构函数会导致禁用移动构造函数和移动赋值运算符的生成。它还阻止了对象从 trivially_destructible。所以,虽然它有效,但这种方法有缺点(这些缺点适用于其他特殊成员)。” – @Matthieu M

有没有办法避免这种不利影响?我试过将 static_assert 移动到 Base 的成员函数和静态成员函数,但在这两种情况下,当派生的 class 不产生编译时错误'实施 static int foo()。这是我一直在使用的代码(根据上面的 SO 线程调整):

#include <type_traits>


template <class T>
class Base {
public:
    //~Base() //This works as expected! Compile error because Bad doesn't implement static int foo()
    //{
    //    static_assert(std::is_same<decltype(T::foo()), int>::value, "ERROR: No 'static int foo()' member provided");
    //}

    static void static_test() //This does not work. No compile time error.
    {
        static_assert(std::is_same<decltype(T::foo()), int>::value, "ERROR: No 'static int foo()' member provided");
    }
};

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

class Bad : public Base<Bad> {
public:
    static double foo() { return 42; };
};

int main()
{
    Good g;
    Bad b;
}

不知道我是否理解正确,但我觉得你想的太复杂了。让编译器为您想要生成的特殊成员生成定义的解决方法是声明它们 default:

struct foo {
    ~foo() {} // prevents the compiler to generate move constructor
    foo(foo&&) = default; // compiler generates a move constructor
}; 

您所拥有的是 class 需要实现的概念。您可以在 class 的定义之后简单地检查它,可能最简单的是 static_assert:

template<typename T>
static constexpr bool assert_is_fooable() {
    static_assert(std::is_same<decltype(T::foo()), int>::value, "ERROR: No 'static int foo()' member provided");
    static_assert(condition_2(), "ERROR: condition 2");
    // More specific error messages given above, this is always
    // true so it can be used in a static_assert
    return true;
}

class Good {
public:
    static int foo() { return 42; };
};

static_assert(assert_is_fooable<Good>());

即使 class 未在其他任何地方使用,它也能正常工作。您还可以将断言放在使用 Good 的地方,期望它具有 static int foo()

这使得 class 可以被简单地破坏并且不会抑制移动构造函数或移动赋值运算符,因为它实际上并没有修改 class.