- ???Microsoft Azure CTO Mark Russinovich: C/C++ should be deprecated??? - 1 Update
- Is this valid C++ code? - 1 Update
- how to specify an 8 byte bool ? - 2 Updates
- attempt to dereference a singular iterator - 2 Updates
- Virtual functions: To const, or not to const? - 5 Updates
| Tim Rentsch <tr.17687@z991.linuxsc.com>: Oct 07 04:00PM -0700 > With very few exceptions. > The only static polymorphism I wholeheartedly approve is > polymorphism of arithmetic operators for built-in types. Not that I disagree necessarily, but can you say what it is about function overloading you don't like? (For the time being let's ignore the larger topic of polymorphism with templates.) |
| Andrey Tarasevich <andreytarasevich@hotmail.com>: Oct 07 12:41PM -0700 > Visual Studio 2022 (v143) says "no", "'f': is not a member of 'Foo<std::basic_string_view<char,std::char_traits<char>>,void>", 17 > at line (*) > Both compiled with C++17. Your program is invalid (ill-formed). However, no diagnostic is required. An issue of the same general nature can be demonstrated by a more simple example class C; template <typename T = int> void foo() { C c; // Incomplete type error? Or not? } int main() { foo(); } class C {}; Note that the above code is also quietly accepted by GCC, but rejected by MSVC and Clang. Briefly and informally, the rule it violates says the following: if the interpretation of your template changes depending on its point of instantiation, the program is ill-formed. In a more focused and formal form: if a reference to an non-dependent name from an imaginary instantiation that immediately follows the definition is invalid, the program is ill-formed. A complete set of formal requirements can be found in [temp.res]: https://timsong-cpp.github.io/cppwp/n4659/temp.res#8.3 https://timsong-cpp.github.io/cppwp/n4659/temp.res#8.4 https://timsong-cpp.github.io/cppwp/n4659/temp.res#temp.point-8 The first two links deal with non-dependent names, while the third one applies to dependent names. But the general idea is the same: if the meaning of a template specialization depends on its point of instantiation, the program is ill-formed. If the meaning of a template specialization changes as you move it up and down in the translation unit, the program is ill-formed. Yet, no diagnostic is required. This is basically it. But there's another detail at play here. The freedom provided by "no diagnostic is required" supplies the implementations with quite a bit of leeway in choosing the point of instantiation. The obvious next logical step is the resolution of DR#993 https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#993 It allows implementations to stop worrying about choosing the proper point of instantiation completely. It allows them to simply push all instantiations to the very end of the translation unit and be done with it. This is an opportunity GCC immediately took advantage of. GCC instantiates templates at the very end of the TU and, expectedly, interprets their content from that vantage point. Which is why GCC reports errors neither in your example nor in mine. Meanwhile, Clang and MSVC still stick to the "early instantiation" approach, which allows them to see the problems in our examples. -- Best regards, Andrey |
| David Brown <david.brown@hesbynett.no>: Oct 07 09:07AM +0200 On 06/10/2022 22:07, Lynn McGuire wrote: > typedef unsigned long long ulongint; /* system-dependent */ > #define fstring fem::str > Lynn An alternative if you want to keep C90 support (well, C90 with "long long" support) would be to include <limits.h> and add some compile-time checks on the sizes. The point is to be sure that the sizes are what you expect them to be. You will also want to change the "logical1" and "integer1" typedefs. It is madness to use a plain "char" for anything other than an ASCII character. You want "signed char" for "integer1", and I would have thought "unsigned char" is the appropriate choice for "logical1". |
| Lynn McGuire <lynnmcguire5@gmail.com>: Oct 07 02:23PM -0500 On 10/7/2022 2:07 AM, David Brown wrote: > is madness to use a plain "char" for anything other than an ASCII > character. You want "signed char" for "integer1", and I would have > thought "unsigned char" is the appropriate choice for "logical1". I don't use those types in my code so I don't care. Those types are just in the standard f2c.h declaration. Thanks, Lynn |
| Jivanmukta <jivanmukta@poczta.onet.pl>: Oct 07 07:34AM +0200 W dniu 6.10.2022 o 08:55, Juha Nieminen pisze: > code until you can't remove anything without making the problem > disappear. Anything that doesn't affect the problem, remove, until > you have the minimal amount of code that still exhibits the problem. I will try do this but it won't be easy task - my program has nearly 10 KLOC. |
| Jivanmukta <jivanmukta@poczta.onet.pl>: Oct 07 03:20PM +0200 W dniu 7.10.2022 o 07:34, Jivanmukta pisze: > I will try do this but it won't be easy task - my program has nearly 10 > KLOC. Moreover, my program consists of many highly-coupled files. |
| Juha Nieminen <nospam@thanks.invalid>: Oct 07 09:47AM I have encountered this small dilemma several times. It's a question of const correctness and proper OO design in C++. Should a virtual function in a base class be const or not? This especially in cases where it's very possible that a derived implementation wants to change its own state (and thus would want the function to not be const). The answer seems simple enough: Obviously it's better to not make it const, to allow for this possibility. Problem is, when it's not const, now it can't be called using a const instance, or const reference or pointer to one. There may be situations where the only thing you have is a const version of the object, and there's no way around that. (Ok, technically speaking you *can* call it, by using const_cast. But that's a really ugly solution, bad design, and in some cases might even potentially break something. (Const code might assume the object doesn't change.)) "Well, in that case offer *both* versions of the virtual function in the base class, duh! Problem solved!" Except that sometimes, in some situations, there may not be any reasonable way to implement a const version of a function, because it *has to* change the object in order to function correctly. Sometimes a const version just doesn't make any sense. (For example, a const version of std::vector::push_back() wouldn't make much sense. What would it be supposed to do? Nothing? That would make it really odd design, and would have the potential to break something.) This isn't even a question of "const correctness". It's a question of getting a compiler error if all you have is a const reference (and no way around that) and the virtual function happens to be non-const (even though the implementation doesn't actually change anything). |
| David Brown <david.brown@hesbynett.no>: Oct 07 12:50PM +0200 On 07/10/2022 11:47, Juha Nieminen wrote: > instance, or const reference or pointer to one. There may be situations > where the only thing you have is a const version of the object, and > there's no way around that. Shouldn't it be a matter of deciding whether the function logically changes the object or not? If it clearly changes the object, it must be non-const. But if it leaves the object with the same publicly visible state, make it const. Any later bits that might need changing should be mutable. A virtual function is an interface, not implementation detail. It doesn't make sense to me to have a virtual function that is logically const in a base class, but logically non-const in a derived class. |
| Paavo Helde <eesnimi@osa.pri.ee>: Oct 07 02:00PM +0300 07.10.2022 12:47 Juha Nieminen kirjutas: > in cases where it's very possible that a derived implementation wants > to change its own state (and thus would want the function to not be > const). IMO, the 'const' should be applied logically. There is a virtual function which does something. Is this something that conceptually should not modify the object state? If yes, then make the virtual function const. If a derived class overrides this function, it should conceptually still do something which does not modify the object. If it conceptually does, then it's possible you have bigger problems with the design, starting with the function name. If the derived class modifies its state only technically, e.g. by caching something, we have means to express this in C++ cleanly - that's the 'mutable' keyword. When following these rules, now if you have a const reference to the object and need to call a non-const virtual function on it, this means you want to call a function on a const object which might (again, conceptually) modify the object state. That's a design smell, and this is the exact reason why the language has the const qualifier in the first place - to catch such things at compile time so the design could be fixed. In reality the things won't work out so nicely. In some cases the base class virtual interface is at so abstract level it is impossible to say if the functions might conceptually modify the object or not. According to the above guidelines these virtual functions should be non-const, which in turn means that this class cannot be really used via const references. In my code I have got several "non-const" classes which are basically always accessed only over non-const references. Yes, this looses the benefits of 'const', but at least it is honest about that and does not hide it behind a wall of const_cast-s. Other scenario is e.g. with virtual functions which take callbacks or functors as arguments. Some callbacks might want to modify the object, some not. In that case one should provide two different virtual functions, one const and the other non-const. If there is some override which does not make sense and which should not be called, then it's easy for me. I just put a debug assert failure and a runtime exception there. hth Paavo |
| Richard Damon <Richard@Damon-Family.org>: Oct 07 07:29AM -0400 On 10/7/22 5:47 AM, Juha Nieminen wrote: > (and no way around that) and the virtual function happens to be > non-const (even though the implementation doesn't actually change > anything). The answer is that if the state isn't part of the visible state, make the state mutable, which means it CAN be changed in const objects and not giv problems. If the state is part of the visible state, then you shouldn't really do that, as const functions shouldn't visible change the state, but it says the overriding doesn't really apply. A caller thining they are doing a constant operation on an object shouldn't find the object having changed. You have a design issue is you have an operation that inherently might change the object but also needs to be implemented on const objects. THAT is your problem. |
| "Öö Tiib" <ootiib@hot.ee>: Oct 07 05:55AM -0700 On Friday, 7 October 2022 at 12:47:29 UTC+3, Juha Nieminen wrote: > in cases where it's very possible that a derived implementation wants > to change its own state (and thus would want the function to not be > const). If it is not changing base class state only because the base class is kind of dummy/refusing but otherwise it is logical to change state then of course do not make method const. If it changes derived state only because of some internal efficiency or safety (caching, reporting, lazy loading) but otherwise it is logical not to change state then make member const but that state mutable. > instance, or const reference or pointer to one. There may be situations > where the only thing you have is a const version of the object, and > there's no way around that. If it logically mutates state of object then that is good that it can not be called on const object. > that's a really ugly solution, bad design, and in some cases might > even potentially break something. (Const code might assume the object > doesn't change.)) Yes. That can break something. > Except that sometimes, in some situations, there may not be any > reasonable way to implement a const version of a function, because > it *has to* change the object in order to function correctly. So you want the call to compile on const object despite it can't be done on the const version? Then the const version can throw or refuse in some other manner. > sense. What would it be supposed to do? Nothing? That would make > it really odd design, and would have the potential to break > something.) Then it is perfect that attempt to call push_back() on const object gives compiling error. > (and no way around that) and the virtual function happens to be > non-const (even though the implementation doesn't actually change > anything). We can't have some kind of pre-decided silver bullet here. On some cases we make virtual function non-const on other cases const and on third cases provide both overloads. Logic is more or less same as without dynamic polymorphism. As always there can be some kind of grey area where it is not crisp clear what to do but such example needs to be looked at and decided concretely and individually, general rules are hard to establish. |
| You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment