比较对象可调用为 const

Posted

技术标签:

【中文标题】比较对象可调用为 const【英文标题】:Comparison object being invocable as const 【发布时间】:2018-12-16 12:51:58 【问题描述】:

当我尝试运行以下代码时,带有 -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 : 错误: static_assert 由于要求'is_invocable_v'而失败“比较对象必须作为常量调用”

叮当 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&amp;&lt; 更改为 const _Compare&amp;is_invocable 更改为 is_invocable_v,尽管据我所知,这只是为了获得 inline 和 constexpr @987654321 @


我已经找到this链接,基于源代码注释,但我仍然无法理解为什么这是必需的。

【问题讨论】:

比较运算符应该一直是 const。现在正在积极执行这条潜规则。 @SamVarshavchik 好的,但是为什么它们应该是 const 呢?有什么好处还是有什么历史原因导致它发生? 因为比较器类的实例是容器的一部分,除非该方法是const,否则不能调用const类实例的方法。 @SamVarshavchik 这确实有道理,但是为什么 c++14 允许这样做呢?是因为在 c++14 中没有 set 的 const 方法,还是纯粹是一个忽略? 好吧,它只是在某种意义上“允许”的,如果您使用仅在容器的可变实例上使用比较器类的类方法,它仍然可以工作。但是尝试在const 容器上使用它们仍然会失败,因为它从根本上是不正确的。因此,这是严格执行const-正确性。这总是一件好事;而且我认为浪费时间弄清楚为什么据称它在 C++14 中仍然“允许”它几乎没有什么好处。这完全无关紧要。任何这样做的代码都被破坏了,应该被修复。 【参考方案1】:

将操作符设置为 const,因为它应该是(不允许可变状态):

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

如果您跨线程并行运行此比较器,则 constness 对安全很有好处。默认情况下,它还可以防止奇怪的副作用,并允许编译器进行更多优化。如果 stdlib 允许操作符是非常量的,它还应该假设有一些状态正在被修改(非常量),因此访问可能不是线程安全的,或者它可能不会随意复制(并行访问)。

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

【讨论】:

【参考方案2】:

这不是一个真正的答案,而是一个说明性的例子:

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

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

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

【讨论】:

以上是关于比较对象可调用为 const的主要内容,如果未能解决你的问题,请参考以下文章

C++ Primer 5th笔记(chap 16 模板和泛型编程)转发

Firebase Cloud Functions shell:添加身份验证令牌以调用可调用对象

让类成员函数指针成为可调用对象

C++高级开发之可调用对象functionbind

C++高级开发之可调用对象functionbind

C++高级开发之可调用对象functionbind