Skip to content

Instantly share code, notes, and snippets.

@jmlvanre
Last active August 29, 2015 14:18
Show Gist options
  • Save jmlvanre/8425725ee72d41917b2d to your computer and use it in GitHub Desktop.
Save jmlvanre/8425725ee72d41917b2d to your computer and use it in GitHub Desktop.
New GCC Features 4.4 => 4.7
  • Range-based For Loop:

    • We can now write for loops as such:
    std::map<K, V> data;
    for (const std::pair<K, V>& pair : data) {}
    for (const auto& pair : data) {}
  • Explicit Virtual Override:

    • We can now introduce a safety measure on virtual function override so that if the base prototype changes, our program will no longer compile until the override definition has been changed to match. This is a great safety measure in a growing code base.
    class Base {
    public:
      virtual int Func(int arg) = 0;
      virtual ~Base() {}
    };
    class Derived : public Base {
    public:
      // This will not compile until we change to: int Func(int arg) override {}.
      virtual void Func() override { ... }
      virtual ~Derived() {}
    };
  • Lambdas:

    • old way:

      void _send() {
        VLOG(2) << "Finished send";
      }
      void send() {
        socket.send("Hello World!").then(lambda::bind(_send));
      }
    • new way:

      void send() {
        socket.send("Hello World!").then(
        []() -> void {
          VLOG(2) << "Finished send";
        });
      }
  • Nullptr:

    We can now replace all instances of 'NULL' with 'nullptr':

    T* ptr = nullptr;
  • Unrestricted Unions:

    We can now have non-pod types in unions. This allows us to

    • Overlap storage allocation for objects that never exist simultaneously:
    #include <string>
    
    struct T {
      enum {Str, Int, Double, Float} kind;
      T() : kind(Int) {}
      union Data {
        Data() : int_(5) {}
        ~Data() {}
        std::string str;
        int int_;
        double double_;
        float float_;
      } data;
    };
    
    int main() {
      printf("size of T = [%ld] bytes\n", sizeof(T));
    }

    output:

    size of T = [16] bytes
    
    • Avoid the requirement of a default constructed object if it is a member of the union:
    #include <vector>
    
    class Obj {
      Obj(int _arg) : arg(_arg) {}
      private:
      Obj() = delete;
      int arg;
    };
    
    struct T {
      T() {}
      ~T() {}
      union {
        Obj obj;
      };
    };
    
    int main() {
      // This would complain about Obj not having a default constructor.
      //std::vector<Obj> data(1);
    
      // This works because the union only reserves space in T, it doesn't initialize obj. 
      // Now we've allocated space for an Obj, without forcing a default constructor.
      std::vector<T> data(1);
    }
  • Constant Expressions:

    We can now pass expressions where we could not before:

    • Given:

      #include <unistd.h>
      #include <stdio.h>
      
      struct HttpRequest {
        char buf[12];
      };
      
      struct HttpResponse {
        char buf[16];
      };
      
      template <size_t size>
      struct Buffer {
        char buf[size];
      };
    • Doesn't work:

      #include <algorithm>
      int main() {
        // This won't compile because std::max is not a constexpr.
        Buffer<std::max(sizeof(HttpRequest), sizeof(HttpResponse))> buf;
        printf("size of buf = [%ld]\n", sizeof(buf));
      }
    • Now we can do:

      template <typename T>
      constexpr const T& max(const T& lhs, const T& rhs) {
        return lhs > rhs ? lhs : rhs;
      }
      int main() {
        Buffer<max(sizeof(HttpRequest), sizeof(HttpResponse))> buf;
        printf("size of buf = [%ld]\n", sizeof(buf));
      }
  • Delegating Constructors:

    We can now simplify classes by turning repeated logic used by multiple constructors into constructor delegation:

    • Before:
    // We used to also use a private init() routine to avoid this pattern. 
    // Still, constructor delegation is more clean and concise!
    class Foo {
    public:
      Foo (int arg1, int arg2, int arg3) {
        CHECK(arg1 > 0) << "Foo::arg1 must be > 0";
        ...
      }
      Foo (const std::string& name, int arg1, int arg2, int arg3) {
        CHECK(arg1 > 0) << "Foo::arg1 must be > 0";
        ...
      }
      Foo (const Bar& other) {
        arg1 = other.arg1;
        ...
        CHECK(arg1 > 0) << "Foo::arg1 must be > 0";
      }
    private:
      int arg1, arg2, arg3;
    };
    • Now:
    // Delegate construction and perform the logic once!
    class Foo {
    public:
      Foo (int arg1, int arg2, int arg3) {
        CHECK(arg1 > 0) << "Foo::arg1 must be > 0";
        ...
      }
      Foo (const std::string& name, int arg1, int arg2, int arg3) 
        : Foo(arg1, arg2, arg3) { ... }
      Foo (const Bar& other) 
        : Foo(other.arg1, other.arg2, other.arg3) { ... }
    private:
      int arg1, arg2, arg3;
    };
  • Thread-Local Storage:

    We can now use standardized thread-local storage:

    • We can use constant expressions to initialize them
    • They are much more efficient
    • They are standardized! :-)
    • Before:
      #include <stout/thread.hpp>
      ThreadLocal<bool>* my_local_data = new ThreadLocal<bool>(); 
    • Now:
      __thread bool my_local_data = false;
  • Template Alias-Declarations:

    from: https://gcc.gnu.org/gcc-4.7/changes.html

    G++ now implements C++11 alias-declarations.

    template <class T> using Ptr = T*;
    Ptr<int> ip;  // decltype(ip) is int*

    These are useful when we want to alias types in templates that have not been fully specialized yet.

  • Non-Static Data Member Initializers:

    from: https://gcc.gnu.org/gcc-4.7/changes.html

    G++ now implements C++11 non-static data member initializers.

    struct A {
      int i = 42;
    } a; // initializes a.i to 42
  • Lots More:

    Check out:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment