Использование 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 ответа

Решение

Если вы хотите, чтобы какая-то перегрузка вызывала ошибку компиляции, когда она выбрана в разрешении перегрузки, вы можете использовать новую = delete особенность.

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

Это сгенерирует приятную простую ошибку в духе "использования удаленной функции". void add_child(Args&&...) const".

static_assert должен зависеть от параметра шаблона, который будет отложен после создания шаблона. В противном случае они будут "вызваны", как только будет доступна вся информация, в вашем случае false готов довольно рано в компиляции.

Чтобы решить эту проблему, я обычно заставляю это "искусственно" зависеть от параметра шаблона, как в:

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

Может быть, в вашем случае просто удаление функции может быть лучшим подходом.

Другие вопросы по тегам