昨天我花了很多时间来处理从非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
也是如此。我还能怎么做才能做到这一点?
答案 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
”。