1

一个例子:

template <typename TDerived>
struct Base
{
    void DoSomething()
    {
        if (static_cast<TDerived*>(this)->CheckSomeFlag())
        {
            std::cout << "true" << std::endl;
        }
        else
        {
            std::cout << "false" << std::endl;
        }
    }

    // Default implementation
    bool CheckSomeFlag()
    {
        return false;
    }
};

struct Derived : public Base<Derived>
{
    bool CheckSomeflag()
    {
        return true;
    }
};

int main(int argc, char** argv)
{
    Derived d;

    d.DoSomething();
    return 0;
}

在这种情况下,程序打印“false”,因为程序员在 Derived::CheckSomeFlag() 的声明中有错字,并且 F 是小写的。我想在编译时使用像 OVERRIDE(x) 这样的宏来捕获这种情况,该宏扩展为 astatic_assert(is_member_of<Base, x>::value, "error");但不确定完成此操作所需的模板技巧。

4

4 回答 4

2

模板和宏的组合可以提供以下功能:

  • 如果未在基类上声明类似的函数,则静态断言。
  • 在派生类中声明函数。

使用这种方法,宏将定义一个类型特征模板,用于测试基类上特定成员函数的存在。当基类具有指定的成员函数时,特征的value成员将是true. 然后value在静态断言中使用类型特征的 。

#define TRAITNAME_HELPER( PREFIX, FN, LN )  PREFIX ## FN ## LN
#define TRAITNAME( FN, LN ) TRAITNAME_HELPER( has_fn, FN, LN )

#define OVERRIDE_IMPL( CLASS, RETURN_TYPE, FN, ARG_TYPES, LN )  \
  /* Type trait used to determine if                            \
   * RETURN_TYPE T::FN( ARG_TYPES ) exists. */                  \
  template < typename T >                                       \
  struct TRAITNAME( FN, LN )                                    \
  {                                                             \
    /* Type that expects a value for the specific type.  For    \
     * example, type_check< int, 4 >. */                        \
    template < typename U, U > struct type_check {};            \
                                                                \
    /* Use type_check expect a specific                         \
     * pointer-to-member-function on T. */                      \
    template < typename U >                                     \
    static std::true_type                                       \
    check( type_check< RETURN_TYPE (T::*)ARG_TYPES,             \
                       &U::FN >* = 0 );                         \
                                                                \
    template < typename U >                                     \
    static std::false_type check( ... );                        \
                                                                \
    /* Determine which check function was resolved for T. */    \
    typedef decltype( check< T >( 0 ) ) type;                   \
    static constexpr decltype(type::value) value = type::value; \
  };                                                            \
  static_assert( TRAITNAME( FN, LN )< CLASS >::value,           \
                 "" #RETURN_TYPE " " #FN #ARG_TYPES             \
                 " is not defined in " #CLASS "." );            \
  RETURN_TYPE FN ARG_TYPES
#define OVERRIDE( CLASS, RETURN_TYPE, FN, ARG_TYPES )           \
  OVERRIDE_IMPL( CLASS, RETURN_TYPE, FN, ARG_TYPES, __LINE__ )
  • OVERRIDE_IMPL 宏末尾缺少分号允许在类中声明或定义成员函数。
  • 需要额外级别的宏来支持重载方法。它__LINE__用于创建独特的类型特征。

使用以下Base课程:

template < typename TDerived >
struct Base
{
  bool CheckSomeFlag();
  bool CheckSomeFlag(int, int);
};

如果Dervied类定义为:

struct Derived : public Base< Derived >
{
  OVERRIDE( Base< Derived >, bool, CheckSomeflag, () );
};

然后编译失败并出现以下错误(demo):

error: static assertion failed: "bool CheckSomeflag() is not defined in Base< Derived >."

但是,当类型正确时,它将编译,如下所示

struct Derived : public Base< Derived >
{
  OVERRIDE( Base< Derived >, bool, CheckSomeFlag, () );
  OVERRIDE( Base< Derived >, bool, CheckSomeFlag, (int a, int b) )
  {
    return ( a > b );
  }
};
bool Derived::CheckSomeFlag() { return true; }

这种方法很快就会想到两个缺点:

  • 返回类型必须完全匹配。如果没有一些额外的工作,这可以防止使用协变返回类型。但是,这可能是 CRTP 模式的理想行为。
  • 宏语法掩盖了函数类型。例如,不能bool(int,int)用作返回 abool并具有两个int参数的函数的类型,而是必须将其作为两个单独的参数传递给宏( ..., bool, ..., (int, int) )。这可以通过更改宏期望的参数顺序来缓解,但我选择了与函数的正常声明相匹配的顺序:return-type identifier(args).
于 2012-07-15T21:29:25.640 回答
1

这是 C++11 中的一个想法:

#include <iostream>
#include <type_traits>

struct Foo { void f() { } };
struct Bar : Foo { void f() { } };
struct Zip : Foo {              };

int main()
{
    std::cout << "Bar: " << std::is_same<decltype(&Bar::f), void(Foo::*)()>::value << std::endl
              << "Zip: " << std::is_same<decltype(&Zip::f), void(Foo::*)()>::value << std::endl
    ;
}

这可能会变成静态断言。

于 2012-07-15T15:59:55.527 回答
0

这似乎给你一个编译时错误:

...
template <bool (TDerived::*TPointer)()> class Checker { };

void DoSomething()
{
    static Checker<&TDerived::CheckSomeFlag> foo;
...

这个想法是(不幸的是,我不是模板专家)我只是试图访问 TDerived 中的 CheckSomeFlag 成员,编译器将无法用指向 Base 成员的指针替换它,因为继承只有效另一种方式(或者可能根本没有,我对非虚拟方法不太确定)

于 2012-07-15T18:00:03.967 回答
0

你不能从代码中做到这一点。我建议查看一些静态分析工具,您可以使用它们来捕获常见(而不是那么常见)的程序员错误。他们有很多。一个例子: http: //www.coverity.com/products/static-analysis.html

这是一个常见的程序员错误,基类作者应该意识到这一点。请注意,如果您想提供默认实现但又想强制派生类程序员重写该方法以避免错误,您可以使用以下技巧使用纯虚拟声明:

class Base {
  public:
  virtual void foo() = 0;
};
// Did you know that you can still implement pure virtual functions? Neat!
void Base::foo() {
  // does something useful that might be applicable to most cases
}
class Derived : public Base {
  typedef Base superclass;
  public:
  void foo() {
    // I am forced to override foo
    // But author of Base kindly provided a default implementation
    superclass::foo();
  }
};
于 2012-07-15T15:20:32.563 回答