当我尝试运行以下代码时,带有(-std = c ++ 17)的clang(6.0)和g ++(8)都给我一个static_assert错误:
#include <set>
struct A {};
struct ProcessComparator { inline bool operator()(const A&, const A&) { return true; } };
int main(void)
{
std::set<A, ProcessComparator> A_Set;
return EXIT_SUCCESS;
}
g ++ 8
/ usr / bin /../ lib / gcc / x86_64-linux-gnu / 8 /../../../../ include / c ++ / 8 / bits / stl_tree.h:457:7 :错误:由于要求'is_invocable_v',导致static_assert失败“比较对象必须可作为const调用”
铛6.0
/ usr / include / c ++ / 8 / bits / stl_tree.h:457:21:错误:静态断言失败:比较对象必须可作为const调用
将const作为operator()签名的一部分可以解决此问题:
#include <set>
struct A {};
/* Add const as part of the operator's signature */
struct ProcessComparator { inline bool operator()(const A&, const A&) const { return true; } };
int main(void)
{
std::set<A, ProcessComparator> A_Set;
return EXIT_SUCCESS;
}
同时使用std = c ++ 14,该错误在clang和g ++中均消失了。
我的问题是在c ++ 17中对此进行了哪些更改,现在给出了错误,为什么const在这里很重要?
const仅保证在ProcessComparator类中声明的每个对象都不会被修改(可变对象除外),那么为什么要这样做?
这是静态断言失败的源代码中的源代码:
#if __cplusplus >= 201103L
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
"comparison object must be invocable with two arguments of key type");
# if __cplusplus >= 201703L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2542. Missing const requirements for associative containers
static_assert(is_invocable_v<const _Compare&, const _Key&, const _Key&>,
"comparison object must be invocable as const");
# endif // C++17
#endif // C++11
添加了一个新的static_assert,其中“比较”对象从_Compare&<
更改为const _Compare&
,从is_invocable
更改为is_invocable_v
,尽管据我所知,只是为了获得内联和constexpr as seen here
我已经根据源代码注释找到了this链接,但我仍然不明白为什么这是必需的。
答案 0 :(得分:2)
这实际上不是答案,而是一个说明性的示例:
假设您有一个例程来测试您的集合中是否包含特定值:
template <typename T>
bool contains(const std::set<T> &s, const T& value)
{ return s.find(value) != s.end(); }
如果您的比较函子不能作为const调用,则将无法编译,并显示可怕的错误消息。 (甚至在C ++ 11和14中)
答案 1 :(得分:-1)
使运算符const保持应有的状态(不允许处于可变状态):
struct ProcessComparator { inline bool operator()(const A&, const A&) const { return true; } };
如果跨线程并行运行此比较器,则constness安全性很好。默认情况下,它还可以防止怪异的副作用,并允许编译器进行更多优化。如果stdlib允许运算符为非常量,则还应假定存在某些状态已被修改(非常量),因此访问可能不是线程安全的,或者可能不会故意复制(并行访问)。
虽然编译器可能会自己解决这个问题(但只有在内联的情况下),但库现在强制执行此操作,以帮助您编写更正确,更惯用的代码。