online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code    Language
/****************************************************************************** Welcome to GDB Online. GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby, C#, VB, Perl, Swift, Prolog, Javascript, Pascal, HTML, CSS, JS Code, Compile, Run and Debug online from anywhere in world. Gabriel Staples www.ElectricRCAircraftGuy.com 11 Sept. 2020 Try to solve the connundrum of `ClassWithConst` not having a valid default assignment `operator=()` func since it has a constant member (`i` in this case). *******************************************************************************/ // C++ includes #include <optional> #include <type_traits> // C includes #include <stdio.h> class ClassWithConst { public: const int i; // int i; ClassWithConst(int val) : i(val) { // This static assert FAILS when member `i` is const, since `i` being // const REQUIRES a custom assignment operator below, and once I // define a custom one of those, the class is no longer trivially // copyable, by definition! // static_assert(std::is_trivially_copyable<ClassWithConst>()); } // Assignment operator (=): // ClassWithConst& operator=(const ClassWithConst& other) = default; ClassWithConst& operator=(const ClassWithConst& other) { printf("assignment operator\n"); // Let's see...is there a legal way to write into const member `i`? // WORKS! Normally, the public member would have to be // `volatile const int i` instead of just `const int i` (see my // OnlineGDB project "change_volatile_const_variable"!), but for // whatever reason, this works anyway with regular `const` class // members! int * i_p = (int*)(&i); // *i_p = 1234; *i_p = other.i; return *this; } }; // Now let's make a class with a const member and NOT do a custom assignment // operator trick to change the const member! Instead, we will try to copy // the class via `std::optional<>` wrapper objects! class ClassWithConst2 { public: const int i; // int i; uint64_t u1 = 64; uint64_t u2 = 65; uint64_t u3 = 66; uint64_t u4 = 67; uint64_t u5 = 68; uint64_t u6 = 69; uint64_t u7 = 70; ClassWithConst2(int val) : i(val) { } }; int main() { printf("Hello World\n"); ClassWithConst c1(10); printf("c1.i = %i\n", c1.i); ClassWithConst c2(c1); printf("c2.i = %i\n", c2.i); ClassWithConst c3(0); printf("c3.i = %i\n", c3.i); // Error produced by this line, since member `i` is const! // main.cpp: In function ‘int main()’: // main.cpp:38:10: error: use of deleted function ‘ClassWithConst& ClassWithConst::operator=(const ClassWithConst&)’ // c3 = c2; // ^~ // main.cpp:11:7: note: ‘ClassWithConst& ClassWithConst::operator=(const ClassWithConst&)’ is implicitly deleted because the default definition would be ill-formed: // class ClassWithConst // ^~~~~~~~~~~~~~ // main.cpp:11:7: error: non-static const member ‘const int ClassWithConst::i’, can’t use default assignment operator c3 = c2; // call assignment operator; same as this!: `c3.operator=(c2);` printf("c3.i = %i\n", c3.i); ClassWithConst c4(7); c3.operator=(c4); // call assignment operator; same as this!: `c3 = c4;` printf("c3.i = %i\n", c3.i); // =============================== printf("\n"); std::optional<ClassWithConst2> opt1 = std::nullopt; printf("opt1.has_value() = %s\n", opt1.has_value() ? "true" : "false"); ClassWithConst2 c20(20); printf("c20.i = %i\n", c20.i); // opt1 = c20; // DOES **NOT** work for member variable `const int i`, but // works fine if it's just `int i`. opt1.emplace(c20); // DOES work for member variable `const int i`! printf("opt1.has_value() = %s\n", opt1.has_value() ? "true" : "false"); printf("(*opt1).i = %i\n", (*opt1).i); // Let's **really** prove that opt1 contains a **copy** of c21, by // obtaining the address of the int at c21 so we can <s>overwrite it</s> // (no, verify it and compare it, which is much safer!) after // it's been destroyed! int * i_p = nullptr; { ClassWithConst2 c21(21); i_p = (int*)(&c21.i); opt1.emplace(c21); printf("opt1.has_value() = %s\n", opt1.has_value() ? "true" : "false"); printf("(*opt1).i = %i\n", (*opt1).i); } // *i_p = 999; // ok, don't actually do this; this is bad and could crash // a program. Instead, just look at the addresses below! printf("opt1.has_value() = %s\n", opt1.has_value() ? "true" : "false"); printf("(*opt1).i = %i\n", (*opt1).i); printf("i_p = %lu; &(*opt1).i = %lu; difference = %li\n", (uint64_t)(i_p), (uint64_t)(&(*opt1).i), (uint64_t)(i_p) - (uint64_t)(&(*opt1).i)); // I wonder...how big is `opt1`? I suspect it **should** be a tiny bit // bigger than the type it holds, `ClassWithConst2`, since it should have // static memory allocation for the entire type it holds PLUS a little // extra for whatever member variables `std::optional<>` has too! printf("sizeof(opt1) = %lu; sizeof(c20) = %lu\n", sizeof(opt1), sizeof(c20)); printf("\n"); std::optional<ClassWithConst2> opt2 = opt1; printf("opt2.has_value() = %s\n", opt2.has_value() ? "true" : "false"); printf("(*opt2).i = %i\n", (*opt2).i); std::optional<ClassWithConst2> opt3 = c20; printf("opt3.has_value() = %s\n", opt3.has_value() ? "true" : "false"); printf("(*opt3).i = %i\n", (*opt3).i); ClassWithConst2 c22(22); // opt3 = c22; // FAILED TO COPY! (compile error) opt3.emplace(c22); // WORKS FINE! printf("opt3.has_value() = %s\n", opt3.has_value() ? "true" : "false"); printf("(*opt3).i = %i\n", (*opt3).i); // opt3 = opt2; // FAILED TO COPY! (compile error) // opt3.emplace(opt2); // compile error opt3.emplace(*opt2); // WORKS PERFECTLY, SINCE `*opt2` IS just `c21`! printf("opt3.has_value() = %s\n", opt3.has_value() ? "true" : "false"); printf("(*opt3).i = %i\n", (*opt3).i); return 0; } /* Conclusions/Notes: My goals: on a class which can NOT have a default assignment operator, copy it from one location to another, to, for example, pass out a copy from a callback lambda in a unit test to an outer layer, for verification (unit testing) purposes. 1. using `volatile const` to allow modifying the `const` location is well-defined behavior, and fine, but a little weird, but I'd approve your PR unless I saw an easier alternative :) 2. Modifying just a `const` is not well-defined behavior I think, but seems to work ONLY if the variable is a member variable of a class, NOT a local const variable, as then the compiler makes assumptions it won't change and doesn't even look again unless it's marked `volatile`. 3. memcpy also works, but technically is also undefined behavior since the class is not trivially copyable. 4. [THIS!] C++17's std::optional<> with `emplace()` seems to work fine and be well-defined behavior, and clean. This is probably the correct approach to take. 5. It's also possible you really don't even need to change this const value, even for unit tests, as maybe you don't even need to use or compare it. If you really do need to change this const value, however maybe it shouldn't be `const`? But, if it really should be `const`, see #4 above and use std::optional. Share this on Stack Overflow as an answer of my own here!: https://stackoverflow.com/questions/4136156/const-member-and-assignment-operator-how-to-avoid-the-undefined-behavior ======================================== SAMPLE OUTPUT: Hello World c1.i = 10 c2.i = 10 c3.i = 0 assignment operator c3.i = 10 assignment operator c3.i = 7 opt1.has_value() = false c20.i = 20 opt1.has_value() = true (*opt1).i = 20 opt1.has_value() = true (*opt1).i = 21 opt1.has_value() = true (*opt1).i = 21 i_p = 140733777606300; &(*opt1).i = 140733777606312; difference = -12 sizeof(opt1) = 8; sizeof(c20) = 4 */

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue