1

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从未被调用。我还能如何完成这项工作?

4

2 回答 2

3

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

为了解决这个问题,我通常让它“人为地”依赖于一个模板参数,就像在:

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

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

于 2013-04-19T09:50:49.283 回答
3

如果您希望某些重载在重载决议中被选中时导致编译错误,您可以使用新= delete功能。

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

这将产生一个类似于“使用已删除功能void add_child(Args&&...) const”的简单错误。

于 2013-04-22T19:53:41.213 回答