Использование 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 никогда не называется. Как еще я могу сделать эту работу?

1

Решение

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

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

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

3

Другие решения

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

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

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

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

3

По вопросам рекламы [email protected]