C++ 确保派生类实现静态方法

C++ 确保派生类实现静态方法,c++,c++11,typetraits,crtp,static-assert,C++,C++11,Typetraits,Crtp,Static Assert,我想确保派生类实现特定的静态方法。我认为这样做应该是可能的使用,也许利用。然而,到目前为止,我发现它相当复杂,似乎我还没有完全理解它,使我无法采用它来满足我的需要 template <class T> class Base { template<typename C> static char(&g(typename std::enable_if<std::is_same<decltype(static_cast<int(C::*

我想确保派生类实现特定的静态方法。我认为这样做应该是可能的使用,也许利用。然而,到目前为止,我发现它相当复杂,似乎我还没有完全理解它,使我无法采用它来满足我的需要

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");
};
到目前为止我试过的是这个

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; };
};
模板
阶级基础
{
静态断言(std::is_same::value,“错误字符串”);
};
派生类:公共基
{
公众:
静态intfoo(inti){return 42;};
};
但是,它并没有告诉我,即使方法正确实现,派生的元素也没有名为foo的元素。此外,在static_assert内的表达式中为foo提供实际参数感觉是错误的

这样的搜索揭示了一个类似的问题,这最终让我找到了检查类型是否有返回迭代器的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");
};
模板
阶级基础
{
模板
静态字符(&g(typename std::enable_if::type*)[1];
模板
静态字符(&g(…)[2];
静态_断言(sizeof(g(0))==1,“错误字符串”);
};
但此代码不会编译,因为会触发断言

所以我的问题是

  • 为什么编译器在我的第一个示例中找不到派生::foo
  • 在示例代码中,
    typename C::const_迭代器(C::*)()const
    的确切含义是什么?它不是一个返回C::const_迭代器且不带参数的常量函数吗?
    C::*
    的确切含义是什么?那么为什么
    int(C::*)(int)
    在我的例子中是错误的呢
  • 如何正确解决我的问题

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

    这是使用CRTP时的一个常见问题:
    Base
    Derived
    的基列表中遇到时被实例化,此时
    Derived
    还不是一个完整的类型,因为它的其余声明还没有被解析。有各种变通办法。对于
    静态断言
    ,您需要延迟断言的实例化,直到
    派生的
    完成。一种方法是将断言放在
    Base
    的成员函数中,您知道该函数必须实例化-析构函数总是一个不错的选择():


    将转换为“指向
    C
    的常量成员函数的指针,不带任何参数并返回
    C::const_迭代器
    ”,其中当然
    typename
    是必要的,以指示依赖名
    C::const_迭代器
    是一个类型。

    你不是解析器,不要标记你的代码!)模板已经很难阅读了;将一行拆分为太多行只会使情况变得更糟。
    “ERROR STRING”
    是断言失败的错误消息选择。正确的选择是As Base和DEVERTIVE是错误的类名,而foo是错误的方法名但这意味着SSCCE,我认为字符串并不重要。然而,“错误字符串”的本意是告诉人们存在一些真正的错误字符串。我选择了“T需要实现一个带有签名的方法…”之类的内容。不过还是要谢谢你的评论。:)当然为什么我一次又一次地犯同样的错误?谢谢你(再次)向我指出这一点;)然而,为foo()提供实际参数仍然让人感到奇怪:/@sigy如果这真的让您感到困扰,您可以使用
    std::declval
    来创建一个“通用”整数右值表达式:
    static_assert(std::is_same::value,“ERROR STRING”)。你也可以考虑<代码> iSysReals而不是<代码> iSySimule。<代码> int(C::*)(int)是错误的,因为<代码>和派生的类型::Foo是<代码> int(*)(int)< /C> >,因为它是<代码>静态< /代码>成员函数。如果需要左值,可以执行
    std::declval()
    。这个答案有一个小问题:写出析构函数会导致禁用移动构造函数和移动赋值运算符的生成。它还可以防止对象被删除。因此,虽然它有效,但这种方法也有缺点(这种缺点也适用于其他特殊成员)。@sigy我对
    std::declval()
    没有问题,其中
    Foo
    是一种类类型。我不知道为什么
    add\u rvalue\u reference
    会导致任何问题。也许你应该为此提出一个新问题。
    typename C::const_iterator(C::*)() const