本文介绍了在C ++ 11中始终将std :: mutex声明为mutable?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 观看Herb Sutter的演讲后你不知道const和mutable ,我不知道我是否应该总是将互斥量定义为mutable?如果是的话,我想同样适用于任何同步的容器(例如 tbb :: concurrent_queue )? 背景:在他的演讲中,他指出const == mutable ==线程安全, std :: mutex 是定义线程安全的。 这个演讲也有相关问题,在C ++ 11中,const是指线程安全的。 编辑: 在这里,我找到了一个相关问题(可能是重复的)。在C ++ 11之前被问过,但是。也许这会产生影响。解决方案否。 虽然将 const 视为线程安全 mutable 为(已经)线程安全, const 仍然基本上与我不会改变这个值。它总是会的。 我有一个长期的思想训练与我一起。 我自己的编程,我把 const 无处不在。如果我有一个值,这是一件坏事,要改变它,除非我说我想。如果你试图有目的地修改一个const对象,你会得到一个编译时错误(易于修复,没有可交付的结果!)。如果你不小心修改了一个非const对象,你会得到一个运行时编程错误,一个编译应用程序中的错误,和头痛。因此,最好在前面一边错误,保持 const 。 例如: bool is_even(const unsigned x) { return(x%2)== 0; } bool is_prime(const unsigned x) { return / *作为读者的练习* /; } template< typename Iterator> void print_special_numbers(const Iterator first,const Iterator last) { for(auto iter = first; iter!= last; ++ iter) { const汽车& x = * iter; const bool isEven = is_even(x); const bool isPrime = is_prime(x); if(isEven&& isPrime) std :: cout<< 特殊号码!<< x } } 为什么 is_even 和 is_prime 标记为 const ?因为从实现的角度来看,改变我正在测试的数字将是一个错误!为什么 const auto& x ?因为我不打算改变这个值,我想编译器喊我,如果我这样做。与 isEven 和 isPrime 相同:此测试的结果不应更改,因此强制执行。 当然 const 成员函数只是一种给出 this 类型的方法的形式 const T * 。它说如果我改变我的一些成员,这将是一个错误的实现。 mutable 说except me。这是逻辑常数的旧概念的来源。考虑他给出的常见用例:互斥量成员。您需要锁定该互斥锁,以确保您的程序是正确的,所以您需要修改它。你不想让函数是非常量,因为修改任何其他成员会是一个错误。所以你使它 const ,并将mutex标记为 mutable 。 这些都与线程安全无关。 我认为新的定义取代了上面给出的旧想法;他们只是从另一个角度,螺纹安全的意见。 现在的观点Herb给出了如果你有 const 函数,他们需要是线程安全的可由标准库安全使用。作为这个的推论,你应该标记为 mutable 的唯一成员是那些已经是线程安全的,因为它们可以从 const function: struct foo { void act()const { mNotThreadSafe =oh crap!const意味着我会是线程安全的! } mutable std :: string mNotThreadSafe; }; 好的,所以我们知道线程安全的东西可以 mutable ,你问:他们应该是吗? 我想我们必须同时考虑两者。从Herb的新观点来看,是的。它们是线程安全的,所以不需要由函数的常数绑定。但只是因为他们可以安全地从 const 的约束中排除并不意味着他们必须是。我仍然需要考虑:如果我修改了那个成员,它是否会在实现中出错?如果是这样,它不需要 mutable ! 这里有一个粒度问题:一些函数可能需要修改成员 mutable 成员,而其他人不会。这就像只想要一些功能有朋友般的访问,但我们只能够整个类的朋友。 (这是一个语言设计问题。) 在这种情况下,你应该在 mutable / p> 当他给一个 const_cast 示例声明它安全时,Herb只是略微过于宽松。考虑: struct foo { void act()const { const_cast< unsigned&>(counter)++; } 无符号计数器; }; 这在大多数情况下是安全的,除非 foo object本身是 const : foo x; x.act(); // okay const foo y; y.act(); // UB! 这是在SO上的其他地方,但 const foo ,意味着计数器成员也是 const ,并修改 const object是未定义的行为。 这就是为什么你应该在 mutable code> const_cast 不会给你相同的保证。如果计数器已标记为 mutable ,则不会是 const object。 好吧,如果我们需要它 mutable 我们只需要在我们没有的情况下小心。当然这意味着所有线程安全的成员应该标记 mutable 然后? 安全成员有内部同步。最简单的例子是某种包装类(不一定是最佳实践,但它们存在): struct threadsafe_container_wrapper { void missing_function_I_really_want() { container.do_this(); container.do_that(); } const_container_view other_missing_function_I_really_want()const { return container.const_view(); } threadsafe_container container; }; 这里我们包装 threadsafe_container 我们想要的成员函数(在实践中会更好的作为一个自由函数)。不需要 mutable 在这里,从旧的角度来看正确性完全胜利:在一个函数中我正在修改容器,这没关系,因为我没有说我不会(省略 const ),在另一个我不修改容器,并确保我保持承诺(省略 mutable )。 我认为Herb正在争论大多数情况, code> mutable 我们也使用某种内部(线程安全)同步对象,我同意。 Ergo他的观点工作大部分时间。但有些情况下,我只是发生有一个线程安全的对象,只是把它当作另一个成员;在这种情况下,我们回到旧的和根本的使用 const 。 After watching Herb Sutter's talk You Don't Know const and mutable, I wonder whether I should always define a mutex as mutable? If yes, I guess the same holds for any synchronized container (e.g., tbb::concurrent_queue)?Some background: In his talk, he stated that const == mutable == thread-safe, and std::mutex is per definition thread-safe.There is also related question about the talk, Does const mean thread-safe in C++11.Edit:Here, I found a related question (possibly a duplicate). It was asked before C++11, though. Maybe that makes a difference. 解决方案 No. However, most of the time they will be.While it's helpful to think of const as "thread-safe" and mutable as "(already) thread-safe", const is still fundamentally tied to the notion of promising "I won't change this value". It always will be.I have a long-ish train of thought so bear with me.In my own programming, I put const everywhere. If I have a value, it's a bad thing to change it unless I say I want to. If you try to purposefully modify a const-object, you get a compile-time error (easy to fix and no shippable result!). If you accidentally modify a non-const object, you get a runtime programming error, a bug in a compiled application, and a headache. So it's better to err on the former side and keep things const.For example: bool is_even(const unsigned x){ return (x % 2) == 0;}bool is_prime(const unsigned x){ return /* left as an exercise for the reader */;}template <typename Iterator>void print_special_numbers(const Iterator first, const Iterator last){ for (auto iter = first; iter != last; ++iter) { const auto& x = *iter; const bool isEven = is_even(x); const bool isPrime = is_prime(x); if (isEven && isPrime) std::cout << "Special number! " << x << std::endl; }}Why are the parameter types for is_even and is_prime marked const? Because from an implementation point of view, changing the number I'm testing would be an error! Why const auto& x? Because I don't intend on changing that value, and I want the compiler to yell at me if I do. Same with isEven and isPrime: the result of this test should not change, so enforce it.Of course const member functions are merely a way to give this a type of the form const T*. It says "it would be an error in implementation if I were to change some of my members".mutable says "except me". This is where the "old" notion of "logically const" comes from. Consider the common use-case he gave: a mutex member. You need to lock this mutex to ensure your program is correct, so you need to modify it. You don't want the function to be non-const, though, because it would be an error to modify any other member. So you make it const and mark the mutex as mutable.None of this has to do with thread-safety.I think it's one step too far to say the new definitions replace the old ideas given above; they merely compliment it from another view, that of thread-safety.Now the point of view Herb gives that if you have const functions, they need to be thread-safe to be safely usable by the standard library. As a corollary of this, the only members you should really mark as mutable are those that are already thread-safe, because they are modifiable from a const function:struct foo{ void act() const { mNotThreadSafe = "oh crap! const meant I would be thread-safe!"; } mutable std::string mNotThreadSafe;};Okay, so we know that thread-safe things can be marked as mutable, you ask: should they be?I think we have to consider both view simultaneously. From Herb's new point of view, yes. They are thread safe so do not need to be bound by the const-ness of the function. But just because they can safely be excused from the constraints of const doesn't mean they have to be. I still need to consider: would it be an error in implementation if I did modify that member? If so, it needs to not be mutable!There's a granularity issue here: some functions may need to modify the would-be mutable member while others don't. This is like wanting only some functions to have friend-like access, but we can only friend the entire class. (It's a language design issue.)In this case, you should err on the side of mutable.Herb spoke just slightly too loosely when he gave a const_cast example an declared it safe. Consider:struct foo{ void act() const { const_cast<unsigned&>(counter)++; } unsigned counter;};This is safe under most circumstances, except when the foo object itself is const:foo x;x.act(); // okayconst foo y;y.act(); // UB!This is covered elsewhere on SO, but const foo, implies the counter member is also const, and modifying a const object is undefined behavior.This is why you should err on the side of mutable: const_cast does not quite give you the same guarantees. Had counter been marked mutable, it wouldn't have been a const object.Okay, so if we need it mutable in one spot we need it everywhere, and we just need to be careful in the cases where we don't. Surely this means all thread-safe members should be marked mutable then?Well no, because not all thread-safe members are there for internal synchronization. The most trivial example is some sort of wrapper class (not always best practice but they exist):struct threadsafe_container_wrapper{ void missing_function_I_really_want() { container.do_this(); container.do_that(); } const_container_view other_missing_function_I_really_want() const { return container.const_view(); } threadsafe_container container;};Here we are wrapping threadsafe_container and providing another member function we want (would be better as a free function in practice). No need for mutable here, the correctness from the old point of view utterly trumps: in one function I'm modifying the container and that's okay because I didn't say I wouldn't (omitting const), and in the other I'm not modifying the container and ensure I'm keeping that promise (omitting mutable).I think Herb is arguing the most cases where we'd use mutable we're also using some sort of internal (thread-safe) synchronization object, and I agree. Ergo his point of view works most of the time. But there exist cases where I simply happen to have a thread-safe object and merely treat it as yet another member; in this case we fall back on the old and fundamental use of const. 这篇关于在C ++ 11中始终将std :: mutex声明为mutable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-22 19:06