使用static_assert()提供更好的编译时错误(比编译器更多)

时间:2013-04-19 09:14:14

标签: c++ c++11 compiler-errors static-assert

昨天我花了很多时间来处理从非const对象调用const成员函数导致的编译时错误,如下例所示:

// my utility header
template<typename derived>
struct crtp_task : task
{
  std::list<task*> list;

  // note: cannot be const, as task::allocate_child() isn't
  template<typename... Args>      
  void add_child(Args&&... args)
  {
    list.push_back(new(task::allocate_child())
                   derived(std::forward<Args>(args)...));
  }
  /* ... */
};

// an application
struct my_task : crtp_task<my_task>
{
  some_data data;
  /* ... */
  my_task(some_data d) data(d) {}
  void generate_children() const    // constant member
  {
    /* ... */
    some_data child_data = /* ... */;
    this->add_child(child_data);    // error: cannot call non-const member
  }
};

clang错误消息是几行而且太神秘了(没有提到const),但是gcc提出了一个更好的错误(尽管更多行,但最终抱怨我忽略了cv限定符)。

因此,为了避免将来出现这种情况,我考虑在我的实用程序标头中使用static_assert()。我天真的做法

// my utility header
template<typename derived>
struct crtp_task : task
{
  std::list<task*> list;

  // note: cannot be const, as task::allocate_child() isn't
  template<typename... Args>      
  void add_child(Args&&... args)
  {
    list.push_back(new(task::allocate_child())
                   derived(std::forward<Args>(args)...));
  }

  // note: catch call from const objects
  template<typename... Args>      
  void add_child(Args&&...) const
  {
    static_assert(false,"add_child() const called");
  }      
  /* ... */
};

失败,因为编译器会立即触发错误,即使从未调用模板void add_child() const也是如此。我还能怎么做才能做到这一点?

2 个答案:

答案 0 :(得分:3)

static_assert必须依赖于模板实例化后延迟的模板参数。否则,只要所有信息都可用,它们就会被“调用”,在你的情况下,false在编译初期就已准备就绪。

要解决这个问题,我通常会将其“人为地”依赖于模板参数,就像在:

中一样
template<class FOO>
void foo( const FOO& )
{
    static_assert(sizeof(FOO)==0,"This function should never be instantiated");
}

也许在您的情况下,删除该功能可能是更好的方法。

答案 1 :(得分:3)

如果在重载决策中选择了一些重载会导致编译错误,则可以使用新的= delete功能。

template<typename... Args>
void add_child(Args&&...) const = delete;

这会产生一个很好的简单错误,就是“使用已删除的函数void add_child(Args&&...) const”。