Forum Replies Created

Viewing 15 posts - 76 through 90 (of 349 total)
  • Author
    Posts
  • RainerRainer
    Keymaster
        Up
        0
        Down
        ::

        Now, I have it. This is what I meant:

         

        #include <ranges>
        #include <deque>
        #include <string>
        #include <vector>
        
        
        auto constrainedTemplateParameter = []<std::ranges::contiguous_range Cont>(Cont cont) { return cont.size(); }; 
        auto abbreviatedFunctionTemplate = [](std::ranges::contiguous_range auto cont) { return cont.size(); }; 
        
        int main() {
        
            std::vector myVec{1, 2, 3};
            std::string myStr= "dasfasdf";
            std::deque myDec{1, 2, 3};
        
            constrainedTemplateParameter(myVec);
            constrainedTemplateParameter(myStr);
            // constrainedTemplateParameter(myDec); ERROR
        
            abbreviatedFunctionTemplate(myVec);
            abbreviatedFunctionTemplate(myStr);
            // abbreviatedFunctionTemplate(myDec); ERROR
            
        }
        

        Here is the example: https://godbolt.org/z/7cGGxWzd3

        in reply to: Replacing CRTP for static polymorphism with concepts #629523
        RainerRainer
        Keymaster
            Up
            1
            Down
            ::

            Based on Driton’s example, here is the simplified one:

            #include <iostream>   
            
            template <typename Derived>
            concept DerivedLike = requires(Derived d)
            {
                d.interface();
            }; 
            
            
            struct Base_Test {
                virtual void interface() {
                    std::cout << "Base Test Implementation\n";
                }
            };
            
            struct Derived_Test: Base_Test {
                void interface() override {
                    std::cout << "Derived Test Implementation\n";
                }
            };
            
            struct Derived_Test2: Base_Test {};
            
            template <DerivedLike Derived>
            void execute(Derived& d){
                d.interface();
            }  
            
            
            int main(){
             
                Base_Test b1;
                Derived_Test db1;
                Derived_Test2 db2;
             
                std::cout << "\nCalling Test\n\n";
             
                execute(b1); 
                execute(db1);
                execute(db2);
            
            }
            

            You can try it out on Compiler Explorer: https://godbolt.org/z/qTEYscvhG.

            in reply to: Question about covariant return types #629522
            RainerRainer
            Keymaster
                Up
                0
                Down
                ::

                My argumentation is different: std::std::unique_ptr<Base> and a std::unique_ptr<Derived> must not have the same physical size. The C++ standard makes no guarantee in this regard. Due to Alf P. Steinbach, this is the reason smart pointer don’t support the covariant return type.

                in reply to: Voice quality #629518
                RainerRainer
                Keymaster
                    Up
                    0
                    Down
                    ::

                    Hi Driton,

                    Thanks. I will fix it,

                    Rainer

                    => DONE

                    in reply to: Line 31 of nullObject.Cpp contains an error? #629493
                    RainerRainer
                    Keymaster
                        Up
                        0
                        Down
                        ::

                        You are right, but you will get a template instantiation error. This means your compile-time error may be very hard to read. If you don’t believe me, try to invoke on an old gcc std::sort on a std::list. You may get more than 100 error lines. Essentially, template instantiation is duck typing at compile time.

                        On the contrary, when you use concepts, you will get a well-defined error message.

                        in reply to: nullObject – strategized locking #629490
                        RainerRainer
                        Keymaster
                            Up
                            0
                            Down
                            ::

                            Q1. You can always choose the most conservative approach and use an exclusive lock like std::mutex under the hood. This is fine, but has a serious performance penalty. By the way, this is the issue the C implementation of Python has with the GIL. Python uses a Global Interpreter Lock (GIL) when the underlying C functionality is called. They want to get rid of the GIL since the beginning of Python. C++ has a meta rule: you don’t pay for what you don’t use. This called the zero-overhead principle. Or as the C++ Core Guidelines state: P.9: Don’t waste time or space. Therefore, using this conservative approach is not option in C++.

                            Q2: The point is that you develop a library that is used in different environment. Consequentially, you have to tailor your library to its use case. Here is the next rule from the C++ Core Guidelines: CP.1: Assume that your code will run as part of a multi-threaded program.

                            RainerRainer
                            Keymaster
                                Up
                                0
                                Down
                                ::

                                Really, did I say this? Lambdas can be used as coroutines, but I never did it. You should only use a coroutine with no state (empty capture group).  Let me think about my statement a few days.

                                in reply to: std::hash in money.cpp #629488
                                RainerRainer
                                Keymaster
                                    Up
                                    1
                                    Down
                                    ::

                                    I use Money as a key inside std::unordered_set<Money>. Value objects like Money are typically used for keys in associative containers.

                                    For that, Money must be hashable and support equality.

                                    #include <iostream>
                                    #include <unordered_set>
                                    
                                    class Money {
                                     public:
                                        Money() = default;
                                        Money(double a): amt(a) {}
                                    
                                        bool operator == (const Money& ) const = default;
                                        double getAmount() const {
                                            return amt;
                                        }
                                     private:
                                        const double amt{0.0};
                                    };
                                    
                                    template<>
                                    struct std::hash<Money> {
                                        std::size_t operator()(Money const& mon) const noexcept {
                                            return std::hash<double>{}(mon.getAmount());
                                        }
                                    };
                                    
                                    int main() {
                                    
                                        std::cout << std::boolalpha << '\n';
                                    
                                        Money money1(0.0);
                                        Money money2;
                                    
                                        std::cout << "money1 == money2: " << (money1 == money2) << '\n';
                                    
                                        std::unordered_set<Money> myMoneySet{money1 , money2};
                                        for(auto mon: myMoneySet) std::cout << "mon.getAmount(): " << mon.getAmount() << '\n';
                                    
                                        std::cout << '\n';
                                    
                                    }
                                    

                                    When I remove the full specialization of std::hash, the program breaks: https://godbolt.org/z/1r9vrKEoh.

                                    in reply to: is_constant_evaluated real world example #629333
                                    RainerRainer
                                    Keymaster
                                        Up
                                        0
                                        Down
                                        ::

                                        There are a few domains that a highly performance driven. For example, the gaming industry or the trading industry. Being faster than the competitor is a big win for them.

                                        in reply to: bridge.cpp example #629332
                                        RainerRainer
                                        Keymaster
                                            Up
                                            1
                                            Down
                                            ::

                                            The combination of the Abstraction and the Implementor is the bridge. They connect between the refined abstraction and the concrete implementation. The concrete implementor is injected into the refined abstraction. The key idea of the bridge is that you can exchange the implementation but also the abstraction.

                                            Typically, you only want to replace the implementation, but not the abstraction. In this case, you don’t need a refined abstraction.

                                            RainerRainer
                                            Keymaster
                                                Up
                                                0
                                                Down
                                                ::

                                                Your solution has a few serious issues: https://godbolt.org/z/cn83q8Ehs

                                                #include <string>
                                                #include <vector>
                                                #include <iostream>
                                                
                                                class Foo {
                                                public:
                                                    Foo(std::string ss, std::vector<int> vv, int ii) : s(std::move(ss)), v(std::move(vv)), i(std::move(ii)){}
                                                
                                                Foo swap(const Foo& foo) noexcept
                                                {
                                                    this -> s = foo.s;
                                                    this -> v = foo.v;
                                                    this -> i = foo.i;
                                                    return *this;
                                                
                                                }
                                                
                                                std::string getString(){
                                                    return this -> s;
                                                }
                                                
                                                private:
                                                std::string s{};
                                                std::vector<int> v{};
                                                int i{};
                                                };
                                                
                                                int main(){
                                                
                                                Foo myFoo(std::string("Hola"), std::vector<int>(3,5), 13);
                                                
                                                std::cout << myFoo.getString() << std::endl; // Hola
                                                
                                                Foo myOtherFoo(std::string("Chau"), std::vector<int>(2,4), 17);
                                                
                                                std::cout << myOtherFoo.getString() << std::endl; // Chau
                                                
                                                std::cout << std::endl;
                                                
                                                myFoo.swap(myOtherFoo);
                                                
                                                std::cout << myFoo.getString() << std::endl; // Chau
                                                std::cout << myOtherFoo.getString() << std::endl; // Chau
                                                
                                                
                                                }
                                                
                                                1. Your swap member function assigns yourself the value of the other foo. In general, this is a memory leak because you would have first to delete yourself before you get a new value.
                                                2. Both values are not swapped. The other foo is still in the same state.
                                                in reply to: about modifyVector() #629303
                                                RainerRainer
                                                Keymaster
                                                    Up
                                                    1
                                                    Down
                                                    ::

                                                    vec.reserve(num): At least, space for num elements of the vector is allocated. You address the capacity of the vector.

                                                    vec.resize(num): The vector gets num additional elements, all of them are initialized. You address the size of the vector.

                                                    Use vec.reserve(num) if you need a big vector, and you want to avoid permanent reallocation. This is an expensive operation.

                                                    The difference between the [] operator and the at operator is that at checks boundaries. at if safe but slower.

                                                    RainerRainer
                                                    Keymaster
                                                        Up
                                                        0
                                                        Down
                                                        ::

                                                        Dividing by 0 is not catchable. It’s such a deep exception. Here is how I typically deal with dividing by 0: https://godbolt.org/z/jGfzeecsh.

                                                        in reply to: inline static member #629276
                                                        RainerRainer
                                                        Keymaster
                                                            Up
                                                            0
                                                            Down
                                                            ::

                                                            You use static, when you want to have state or behavior on the class level.

                                                            Typical use-cases are the singleton or the borg idiom.

                                                            There is also an optimization potential. If a member function can not be inlined, the implicit this pointer must be passed around in an extra register. Thanks to a static member, you can spare the this pointer.

                                                            In C++23, the subscript and call operator can be static. I write about it in my post C++23: More Small Pearls.

                                                            in reply to: Completion document #629263
                                                            RainerRainer
                                                            Keymaster
                                                                Up
                                                                1
                                                                Down
                                                                ::

                                                                You will get a paper of participation. I will send it after the next Friday.

                                                              Viewing 15 posts - 76 through 90 (of 349 total)