关于c ++:比较对象可作为const调用

Comparison object being invocable as const

当我尝试运行以下代码时,带有-std = c ++ 17的clang(6.0)和g ++(8)都给我一个static_assert错误:

1
2
3
4
5
6
7
8
9
10
11
#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: error: static_assert failed due to requirement 'is_invocable_v'"comparison object must be invocable as const"

lang6.0

/usr/include/c++/8/bits/stl_tree.h:457:21: error: static assertion failed: comparison object must be invocable as const

将const作为operator()签名的一部分可以解决此问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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类内部声明的每个对象都不会被修改(可变对象除外),那么为什么要这样做呢?


这是静态断言失败的源代码中的源代码:

1
2
3
4
5
6
7
8
9
10
#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,如此处所示


我已经根据源代码注释找到了此链接,但是我仍然不明白为什么需要这样做。


这实际上不是答案,而是一个说明性的示例:

假设您有一个例程来测试您的集合是否包含特定值:

1
2
3
template <typename T>
bool contains(const std::set< T > &s, const T& value)
{ return s.find(value) != s.end(); }

如果您的比较函子不能作为const调用,则将无法编译,并显示可怕的错误消息。 (即使在C ++ 11和14中)


使操作符为const(应如此)(不允许处于可变状态):

1
struct ProcessComparator { inline bool operator()(const A&, const A&) const { return true; } };

如果跨线程并行运行此比较器,则constness安全性很好。默认情况下,它还可以防止产生怪异的副作用,并允许编译器进行更多优化。如果stdlib允许运算符为非常量,则还应假定存在某些状态被修改(非const),因此访问可能不是线程安全的,或者可能不会故意复制(并行访问)。

虽然编译器可能会自己解决这个问题(但仅在内联的情况下),但库现在强制执行此操作以帮助您编写更正确,更惯用的代码。