New tricks in C++11 that you have found?

Discussion in 'C++' started by Juha Nieminen, Feb 2, 2012.

  1. What are some new fancy tricks that you have found with C++11?
    Perhaps something that was either just outright impossible with C++98
    or much more difficult/laborious. (And naturally I'm not just talking
    about things like "being able to write 'auto' instead of a long type
    name", but less obvious tricks.)

    Here are a couple to start off:

    1) In C++98 it's not possible to get a compile-time constant that's
    eg. the sum of the elements of an array, even if that array is const.
    In C++11 it is now possible by using the new constexpr keyword.

    Example C++11 code:

    //---------------------------------------------------------------
    #include <iostream>

    namespace
    {
    constexpr unsigned kSizes[] = { 5, 15, 8, 4, 28, 3, 5, 17 };
    const unsigned kSizesAmount = sizeof(kSizes) / sizeof(kSizes[0]);

    template<unsigned index>
    struct SizesTotal
    {
    constexpr static unsigned value()
    { return kSizes[index] + SizesTotal<index-1>::value(); }
    };

    template<>
    struct SizesTotal<0U>
    {
    constexpr static unsigned value()
    { return kSizes[0]; }
    };

    constexpr unsigned getSizesTotal()
    { return SizesTotal<kSizesAmount - 1>::value(); }
    }

    int main()
    {
    // This is impossible in C++98:
    unsigned values[getSizesTotal()];

    std::cout << "'values' has " << sizeof(values)/sizeof(values[0])
    << " elements\n";
    }
    //---------------------------------------------------------------


    2) Initializer lists and ranged loops. Technically speaking this is
    possible to do in C++98, but it's more laborious:

    //---------------------------------------------------------------
    #include <iostream>
    #include <vector>

    void print(int t)
    {
    std::cout << t << ", ";
    }

    template<typename T>
    void print(const T &t)
    {
    for(auto& i: t) print(i);
    std::cout << '\n';
    }

    int main()
    {
    auto t1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    std::cout << "--- t1:\n";
    print(t1);

    std::vector<std::vector<int>> t2[] =
    { { { 1, 2 }, { 3, 4 }, { 5, 6 } },
    { { 11, 12, 13 }, { 14, 15 } },
    { { 21 }, { 22, 23, 24, 25, 26 }, { 27, 28, 29 } },
    { { 31, 32, 33, 34, 35, 36, 37 } },
    { { 41 }, { 42 } }
    };
    std::cout << "--- t2:\n";
    print(t2);
    }
    //---------------------------------------------------------------
    Juha Nieminen, Feb 2, 2012
    #1
    1. Advertising

  2. Juha Nieminen

    SG Guest

    On 2 Feb., 18:37, Juha Nieminen wrote:
    > What are some new fancy tricks that you have found with C++11?


    Well, I did not find it myself but here you go:

    C++11 allows function templates to have default template arguments.
    The cool thing about this is that this can be used for SFINAE:

    template<class T,
    class = typename std::enable_if<
    std::is_convertible<int,T>::value
    >::type
    >

    void blank(T & x) {
    x = 0;
    }

    This can also be done for templated conversion operators which was not
    possible in C++98. With a clever macro (original author unknown)

    #define REQUIRES(...) ,class=typename \
    std::enable_if<(__VA_ARGS__)>::type

    we can rewrite the above function template as

    template<class T
    REQUIRES( std::is_convertible<int,T>::value )
    >

    void blank(T & x) {
    x = 0;
    }

    which is almost as good as concepts w.r.t. constraining template
    instantiation.

    Cheers!
    SG
    SG, Feb 3, 2012
    #2
    1. Advertising

  3. On 02.02.2012 18:37, Juha Nieminen wrote:
    > What are some new fancy tricks that you have found with C++11?
    > Perhaps something that was either just outright impossible with C++98
    > or much more difficult/laborious. (And naturally I'm not just talking
    > about things like "being able to write 'auto' instead of a long type
    > name", but less obvious tricks.)


    I find that using lambdas it's possible to express many things more
    cleanly and centralized and DRY, at the cost of sometimes slightly more
    verbose code.

    E.g., capturing a common "pattern" for invoking Windows API functions,


    <code>
    #undef UNICODE
    #define UNICODE
    #undef NOMINMAX
    #define NOMINMAX
    #include <windows.h>

    #include <iostream>
    #include <string>
    #include <stddef.h>
    #include <stdexcept>
    #include <stdlib.h>
    #include <functional>

    using namespace std;

    #ifdef _MSC_VER
    # define CPP_NORETURN __declspec( noreturn )
    # pragma warning( disable: 4646 ) // non-void return type
    #else
    # define CPP_NORETURN
    #endif

    #define CPP_NOTHROW throw()

    typedef ptrdiff_t Size;
    typedef Size Index;

    bool hopefully( bool const v ) CPP_NOTHROW { return v; }
    CPP_NORETURN bool throwX( string const& s ) { throw runtime_error( s ); }


    #if 0
    DWORD WINAPI GetEnvironmentVariable(
    __in_opt LPCTSTR lpName,
    __out_opt LPTSTR lpBuffer,
    __in DWORD nSize
    );
    #endif

    wstring stringResult(
    char const functionIdForErrorMsg[],
    function< Size( wchar_t*, Size ) > const& getText,
    function< Size() > const& getBufferSize = nullptr
    )
    {
    SetLastError( 0 );
    Size const bufSize = (
    !getBufferSize? getText( nullptr, 0 ) : getBufferSize()
    );
    hopefully( bufSize > 0 && GetLastError() == 0 )
    || throwX(
    string() + functionIdForErrorMsg + " failed (getting buffer
    size)"
    );
    wstring result( bufSize, L'@' );
    SetLastError( 0 );
    Size const n = getText( &result[0], result.size() );
    hopefully( n >= 0 && GetLastError() == 0 )
    || throwX(
    string() + functionIdForErrorMsg + " failed (getting text)"
    );
    result.resize( n );
    return result;
    }

    namespace env {
    wstring value( wstring const& name )
    {
    auto const getText = [&name]( wchar_t* pBuf, Size bufSize ) -> Size
    {
    Size const nCharacters =
    GetEnvironmentVariable( name.c_str(), pBuf, bufSize );
    return (nCharacters == 0? -1 : nCharacters);
    };
    return stringResult( "env::value: GetEnvironment", getText );
    }
    } // namespace env

    namespace gui {
    wstring windowText( HWND const window )
    {
    auto const getText = [window]( wchar_t* pBuf, Size bufSize )
    { return GetWindowText( window, pBuf, bufSize ); };

    auto const getBufferSize = [window]()
    { return 1 + GetWindowTextLength( window ); };

    return stringResult(
    "gui::windowText: GetWindowText", getText, getBufferSize
    );
    }
    } // namespace gui

    int main()
    {
    try
    {
    wcout << env::value( L"windir" ) << endl;
    wcout << gui::windowText( GetForegroundWindow() ) << endl;
    return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
    wcerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
    }
    </code>

    Cheers,

    - Alf
    Alf P. Steinbach, Feb 3, 2012
    #3
  4. Juha Nieminen

    U.Mutlu Guest

    Alf P. Steinbach wrote, On 02/03/12 17:58:
    > On 02.02.2012 18:37, Juha Nieminen wrote:
    >> What are some new fancy tricks that you have found with C++11?
    >> Perhaps something that was either just outright impossible with C++98
    >> or much more difficult/laborious. (And naturally I'm not just talking
    >> about things like "being able to write 'auto' instead of a long type
    >> name", but less obvious tricks.)

    >
    > I find that using lambdas it's possible to express many things more cleanly and centralized and DRY, at the cost of sometimes slightly more verbose code.
    >
    > E.g., capturing a common "pattern" for invoking Windows API functions,


    Unfortunately I don't understand what your code does.
    Is it a console application or a gui application?
    It seems to be a console app, but then what is the "gui" code therein?
    Can you comment your code for what it does?
    U.Mutlu, Feb 5, 2012
    #4
  5. Juha Nieminen

    Jorgen Grahn Guest

    On Thu, 2012-02-02, Juha Nieminen wrote:
    > What are some new fancy tricks that you have found with C++11?
    > Perhaps something that was either just outright impossible with C++98
    > or much more difficult/laborious. (And naturally I'm not just talking
    > about things like "being able to write 'auto' instead of a long type
    > name", but less obvious tricks.)


    I'd reformulate that question (or create a new one):

    Given a piece of everyday and well-written C++98 code[1], what constructs
    would a C++11 guy want to replace because they are tedious, error-prone
    or just look archaic?

    I haven't even toyed with the language yet so I have no answer.

    /Jorgen

    [1] Assuming for a moment there is such a thing.

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Feb 5, 2012
    #5
  6. On 05.02.2012 06:50, U.Mutlu wrote:
    > Alf P. Steinbach wrote, On 02/03/12 17:58:
    >> On 02.02.2012 18:37, Juha Nieminen wrote:
    >>> What are some new fancy tricks that you have found with C++11?
    >>> Perhaps something that was either just outright impossible with C++98
    >>> or much more difficult/laborious. (And naturally I'm not just talking
    >>> about things like "being able to write 'auto' instead of a long type
    >>> name", but less obvious tricks.)

    >>
    >> I find that using lambdas it's possible to express many things more
    >> cleanly and centralized and DRY, at the cost of sometimes slightly
    >> more verbose code.
    >>
    >> E.g., capturing a common "pattern" for invoking Windows API functions,

    >
    > Unfortunately I don't understand what your code does.
    > Is it a console application or a gui application?


    Since it uses C++ iostreams for i/o, it works best or at least is
    easiest to use when it's built as a console subsystem app.


    > It seems to be a console app, but then what is the "gui" code therein?


    As the code itself explains, it is a function

    wstring windowText( HWND const window )

    that returns the "text" of a window. In Windows, the "text" of a window
    with caption is the caption, and otherwise it depends. E.g. the "text"
    of an edit control is the text being edited.


    > Can you comment your code for what it does?


    Perhaps ask about the very first thing that is unclear (and only that)?


    Cheers & hth.,

    - Alf
    Alf P. Steinbach, Feb 5, 2012
    #6
  7. Jorgen Grahn <> wrote:
    > Given a piece of everyday and well-written C++98 code[1], what constructs
    > would a C++11 guy want to replace because they are tedious, error-prone
    > or just look archaic?


    That's a completely different question.

    Many of the new constructs not only add shortcuts and easier ways of
    doing things, but in fact also add to the expressiveness of the language.

    The second example in my original post was a good example of that. At
    first glance the ranged for-loop looks simply like syntactic sugar and
    little else. However, it hides quite a lot of expressiveness inside it.
    In the example in question, the crucial code was like this:

    template<typename Container_t>
    void foo(const Container_t& container)
    {
    for(auto& element: container)
    ...
    }

    Note how the function will work with any of the STL containers,
    static arrays and initializer lists. (Technically speaking it's
    possible to make the above function work in the same way with C++98
    (except for initializer lists, of course), but it requires more code.)

    This example might still sound a bit artificial, though. The above
    example function is not something that's very common in actual code.
    However, there are other similar situations that are much more common,
    and where the abstraction provided by the range-based for loop can
    actually be beneficial.

    For example, suppose that you have, let's say, a std::vector as a
    member of a class, and several methods of that class to through the
    entire array to perform operations on its members (or using their
    values). If you later decide that the std::vector can in fact be
    changed to a static array for efficiency, in C++98 you would have
    to go through all the code that loops through the vector and change
    it (if for nothing else, to change the call to the size() method, or
    if you loop using iterators, to change them to pointers). However, if
    you had used range-based for loops, you would not need to change the
    existing code.

    In typical C++98 code a well-designed class has its copy constructor
    and assignment operator disabled, if copying the class would be a
    heavy operation that is not needed. (Implementing working copy
    constructors and assignment operators for a class that handles
    dynamically allocated resources can be laborious, error-prone, and
    in many cases unneeded because the code using the class often doesn't
    need support for that anyways.)

    Rvalue references and copy constructors allow lifting that limitation
    to a degree, without compromising the efficiency of the program. Now
    the code using that class can make copies in certain situations
    (such as returning an object from a function, even in situations where
    the compiler is unable to perform a return-value-optimization trick)
    without having to worry about efficiency issues. Creating temporaries
    and passing them around by value will become efficient and is often
    much cleaner than when copying has been disables completely. And the
    code doesn't even have to pay overhead costs of techniques like
    copy-on-write or reference counting.

    Not being able to specify compile-time constants in an array has
    always been a severe limitation of C++98, one that I have encountered
    in actual programs. C++11 lifts that limitation, which is nice.

    Many of the new convenience features help make the code cleaner.
    For instance, if you wanted to make a small struct (which should be
    as efficient as possible) where you want to make sure that all members
    are properly initialized, in C++98 you have to write an inline constructor
    for the struct, like:

    struct Something
    {
    int a, b, c;

    Something(): a(1), b(10), c(25) {}
    };

    This is not horrible, but it's always a slight nuisance. In C++11,
    however, it becomes much cleaner:

    struct Something
    {
    int a = 1, b = 10, c = 25;
    };

    (Moreover, in C++98 there's no way to initialize a member static array
    even with a constructor initialization list, while in C++11 you can do
    it "inline" like that, or in the constructor initialization list if you
    like.)

    The 'auto' keyword not only can make things cleaner, like this:

    std::map<std::string, std::string> theMap;
    auto iter = theMap.begin();
    ...

    but can also be actually useful in abstracting the code, like so:

    auto someFunctor = boost::bind(&some_function, _2, _1, some_object);

    (In this example we simply don't care, nor should have to care,
    what the type returned by std::bind is, so the 'auto' keyword fits
    perfectly for this purpose.)
    Juha Nieminen, Feb 5, 2012
    #7
  8. Juha Nieminen

    Jorgen Grahn Guest

    On Sun, 2012-02-05, Juha Nieminen wrote:
    > Jorgen Grahn <> wrote:
    >> Given a piece of everyday and well-written C++98 code[1], what constructs
    >> would a C++11 guy want to replace because they are tedious, error-prone
    >> or just look archaic?

    >
    > That's a completely different question.


    I hoped my posting might set you off ;-).

    > Many of the new constructs not only add shortcuts and easier ways of
    > doing things, but in fact also add to the expressiveness of the language.


    [snip good stuff]

    I was approaching it from the other direction though ... there are
    ways we work around restrictions in the language out of habit, and it
    can be hard to realize, when the restriction is lifted, that it
    applies to a specific piece of code. We may have forgotten that what
    we do /is/ a workaround in the first place!

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Feb 5, 2012
    #8
  9. Juha Nieminen

    Rui Maciel Guest

    Alf P. Steinbach wrote:

    > I find that using lambdas it's possible to express many things more
    > cleanly and centralized and DRY, at the cost of sometimes slightly more
    > verbose code.


    Lambdas are indeed a great addition to the language. I can't say that
    enough. If I had to pick only one feature from the lot, I would pick
    lambdas, with range-base for loops at a distant second. These features may
    amount to nothing more than syntactic sugar, but it's some sweet syntactic
    sugar allright.


    Rui Maciel
    Rui Maciel, Feb 5, 2012
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Turamnvia Suouriviaskimatta

    Teaching new tricks to an old dog (C++ -->Ada)

    Turamnvia Suouriviaskimatta, Mar 5, 2005, in forum: C++
    Replies:
    836
    Views:
    10,793
    Ioannis Vranos
    Apr 4, 2005
  2. ennio
    Replies:
    2
    Views:
    864
    Peter Van Weert
    Sep 7, 2006
  3. shoplifes
    Replies:
    0
    Views:
    309
    shoplifes
    Nov 25, 2007
  4. Emmanuel Oga
    Replies:
    2
    Views:
    150
    Emmanuel Oga
    Mar 10, 2008
  5. winter heat
    Replies:
    0
    Views:
    99
    winter heat
    Nov 1, 2010
Loading...

Share This Page