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

J

Juha Nieminen

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);
}
//---------------------------------------------------------------
 
S

SG

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 said:
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
void blank(T & x) {
x = 0;
}

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

Cheers!
SG
 
A

Alf P. Steinbach

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
 
U

U.Mutlu

Alf P. Steinbach wrote, On 02/03/12 17:58:
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?
 
J

Jorgen Grahn

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.
 
A

Alf P. Steinbach

Alf P. Steinbach wrote, On 02/03/12 17:58:

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
 
J

Juha Nieminen

Jorgen Grahn said:
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.)
 
J

Jorgen Grahn

Jorgen Grahn said:
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
 
R

Rui Maciel

Alf said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top