Вчера мне понадобились целые годы, чтобы разобраться с ошибкой во время компиляции, вызванной вызовом 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
никогда не называется. Как еще я могу сделать эту работу?
Если вы хотите, чтобы какая-то перегрузка вызывала ошибку компиляции, когда она выбрана в разрешении перегрузки, вы можете использовать новую = 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");
}
Может быть, в вашем случае, просто удаление функции может быть лучшим подходом.