The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
As VS2012’s C++ compiler doesn’t support “true” variadic templates, the new runtime library classes that use variadic templates are implemented using macro magic behind the scenes. In order to get the “variadic” templates to accept more than the default of five parameters, you’ll have to set _VARIADIC_MAX to the desired maximum number of parameters (between five and ten). For more information, see the “faux variadics” section of this blog post on MSDN.Capturing lvalue references in C++11 lambdas
Pete Barber from C#, C++, Windows & other ramblings
Recently the question "what is the type of an lvalue reference when captured by reference in a C++11 lambda?" was asked. It turns out that it's a reference to whatever the original reference was too. This is just like taking a reference to an existing reference, e.g.int foo = 7;
int& rfoo = foo;
int& rfoo1 = rfoo;
int& rfoo2 = rfoo1;
All references refer to foo rather than rfoo2->rfoo1->rfoo->foo meaning the following code
std::cout << "foo:" << foo << ", rfoo:" << rfoo
<< ", rfoo1:" << rfoo1 << ", rfoo2:" << rfoo2
<< '\n';
++foo;
std::cout << "foo:" << foo << ", rfoo:" << rfoo
<< ", rfoo1:" << rfoo1 << ", rfoo2:" << rfoo2
<< '\n';
std::cout << "&foo:" << &foo << ", &rfoo:" << &rfoo
<< ", &rfoo1:" << &rfoo1 << ", &rfoo2:" << &rfoo2
<< '\n';
Which gives:
foo:7, rfoo:7, rfoo1:7, rfoo2:7
foo:8, rfoo:8, rfoo1:8, rfoo2:8
&foo:00D3FB0C, &rfoo:00D3FB0C, &rfoo1:00D3FB0C, &rfoo2:00D3FB0C
I.e. all the references are aliases for the original foo hence the same value is displayed including when the original is modified and that the address of each variable is the same, that of foo.
There is nothing surprising here it's just basic C++ but it's along time since I've thought about it which is why with lambdas, l-value, r-value and universal references I sometimes I do a double take on what was once obvious.
The same happens with lambda capture but it's a slightly more interesting story. Take the following example:
int foo = 99;
int& rfoo = foo;
int& rfoo1 = foo;
std::cout << "foo:" << foo << ", rfoo:" << rfoo
<< ", rfoo1:" << rfoo1
<< '\n';
std::cout << "&foo:" << &foo << ", &rfoo:" << &rfoo
<< ", &rfoo1:" << &rfoo1
<< '\n';
auto l = [foo, rfoo, &rfoo1]()
{
std::cout << "foo:" << foo << '\n';
std::cout << "rfoo:" << rfoo << '\n';
std::cout << "rfoo1:" << rfoo1 << '\n';
std::cout << "&foo:" << &foo << ", &rfoo:"
<< &rfoo << ", &rfoo1:" << &rfoo1
<< '\n';
};
foo = 100;
l();
Which gives:
foo:99, rfoo:99, rfoo1:99
&foo:00D3FB0C, &rfoo:00D3FB0C, &rfoo1:00D3FB0C
foo:99
rfoo:99
rfoo1:100
&foo:00D3FAE0, &rfoo:00D3FAE4, &rfoo1:00D3FB0C
To begin with it behaves as per the first example in that foo, rfoo and rfoo1 all give the same value as rfoo and rfoo1 are effectively aliases for foo as shown when displaying their addresses; they're all the same.
However, when these same variables are captured it's a different story: The capture of foo is of no surprise as this is by-value so displays the captured value of 99 despite the original foo being changed to 100 prior to the lambda being invoked. Its address is that of a new variable; a member of the lambda.
It starts to get interesting with the capture of rfoo. When the lambda is invoked this too displays 99, the original captured value. Also, its address is not that of the original foo. It seems that the reference itself has not been captured but rather what it refers too, in this case an int with the value of 99. It appears to have been magically dereferenced as part of the capture.
This is the correct behaviour and when thought about becomes somewhat obvious. It's just like assigning a variable from a reference, e.g.
int foo = 7;
int& rfoo = foo;
int bar = rfoo;
bar doesn't become an int& and rfoo is magically dereferenced except in this scenario there is nothing magical at all, it's as expected. If int were replaced with auto, e.g.
auto bar = rfoo;
then it would be expected that bar is an int as auto strips of CV and reference qualifiers.
Finally, there is rfoo1. This too is odd as it is attempting to take a reference to a reference. As seen in the first example this is perfectly fine. The end effect is that there can't be a reference to reference and so on and all are aliases of the original variable.
This is pretty much what's happening here. It's irrelevant that the target of the capture is a reference. In the end the capture by reference is capture by reference of the underlying variable, i.e. what rfoo1 refers too, in this case foo not rfoo1 itself. This is demonstrated twofold by rfoo1 within the lambda displaying the updated value of foo and also that the address of rfoo1 within the lambda is that of foo outside it.
This is as per the standard section 5.1.2 Lambda expression sub-note 14:
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly
captured with a capture that does not include an &. For each entity captured by copy, an unnamed nonstatic
data member is declared in the closure type. The declaration order of these members is unspecified.
The type of such a data member is the type of the corresponding captured entity if the entity is not a
reference to an object, or the referenced type otherwise. [ Note: If the captured entity is a reference to a
function, the corresponding data member is also a reference to a function. —end note ]
The sentence in bold states that for a reference captured by value then the type of the captured value is the type referred to, i.e. the reference aspect as been removed the crucial part being "or the referenced type otherwise". (NOTE: I haven't experimented with references to functions).
Finally, a vivid example showing that a reference captured by value involves a dereference.
class Bar
{
private:
int mValue;
public:
Bar(const Bar&) : mValue(9999)
{
}
public:
Bar(const int value) : mValue(value) {}
int GetValue() const { return mValue; }
void SetValue(const int value) { mValue = value; }
};
Bar bar(1);
Bar& rbar = bar;
Bar& rbar1 = bar;
std::cout << "&bar:" << &bar << ", &rbar:" << &rbar<< ", &rbar1:" << &rbar1 << '\n';
auto l2 = [bar, rbar, &rbar1]()
{
std::cout << "bar:" << bar.GetValue() << '\n';
std::cout << "rbar:" << rbar.GetValue() << '\n';
std::cout << "rbar1:" << rbar1.GetValue() << '\n';
std::cout << "&bar:" << &bar << ", &rbar:" << &rbar<< ", &rbar1:" << &rbar1 << '\n';
};
bar.SetValue(2);
l2();
The class bar provides a crude copy-constructor that sets the stored value to 9999. The following output is similar to that in the previous example in that the addresses of bar and rbar in the lambda differ from that of bar showing they're copies whilst rbar1 is the same. Secondly, the value of mValue stored within Bar is shown as 9999 for the first two captured variables meaning they were copy-constructed.
&bar:00D3FB0C, &rbar:00D3FB0C, &rbar1:00D3FB0C
bar:9999
rbar:9999
rbar1:2
&bar:00D3FAE0, &rbar:00D3FAE4, &rbar1:00D3FB0C
1>------ Build started: Project: References, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\pete\desktop\references\references\main.cpp(85): error C2248: 'Bar::Bar' : cannot access private member declared in class 'Bar'
1> c:\users\pete\desktop\references\references\main.cpp(59) : see declaration of 'Bar::Bar'
1> c:\users\pete\desktop\references\references\main.cpp(54) : see declaration of 'Bar'
1> c:\users\pete\desktop\references\references\main.cpp(59) : see declaration of 'Bar::Bar'
1> c:\users\pete\desktop\references\references\main.cpp(54) : see declaration of 'Bar'
Writing this post has clarified the situation for me, I hope it helps you as well.
The sample code is available here.
Want to bet there were computers involved?
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
The “latest” ACCU magazine showed up a couple of days ago after a minor delay: My guess from my minor experience in software and IT is that “computer says ‘send to India’” :).How I learned about delete-selection-mode
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
One thing I really like about stackoverflow.com is that you end up learning as much answering questions on there as you do by asking them. For example, when I saw this question I was sure there would be a way to delete a region by simply starting to type after selecting the region, but I didn’t know how. However given that this is emacs, I seriously doubted the person asking the question would be the first one to want this particular feature.Updated ‘using namespace xxx;’ blog post
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
I’ve finally corrected some of the mistakes that I published in my blog post about the misuse of using namespace xxx. My apologies for taking this long. Enjoy!Repost – how to get rid of those pesky ^M characters using Emacs
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
I had another of these annoying mixed-mode DOS/Unix text files that suffered from being edited in text editors that didn’t agree which line ending mode they should use. Unfortunately Emacs defaults to Unix text mode in this case so I had an already ugly file that wasn’t exactly prettified by random ^M characters all over the place. I also don’t have the cygwin tools on the machine that I was seeing this problem on, I couldn’t just run unix2dos or dos2unix over the file and be done with it, but at least I had emacs on that machine.A couple of useful Emacs modes
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
This is a repost from my old blog - I’m moving some of my older articles over as nobody knows how long the machine that hosts that blog will still be around. highlight-changes-mode – as the name implies, it highlights changes that you make to a file. I do find it useful for the typical scenario of checking out a file, making a couple of smaller changes to it and then having to diff it to work out what you actually changed.If we consider software creation a craft, Is it time to ‘bring our own tools’?
The Lone C++ Coder's Blog from The Lone C++ Coder's Blog
If you look at really productive programmers - like the top 10-20% - there are usually a couple of characteristics that they share. Aptitude and in-depth understanding of both the system they are working on and the technologies involved is obviously one very important factor. Another factor that tends to be overlooked is that these programmers are also masters of their tools in the same way that a master craftsman - say, a carpenter - is also a master of their tools.Effective C++11/14
Frances Buontempo from BuontempoConsulting
Work sent me on Scott Meyers' latest course at Developer Focus. He gave us these guidelines:- Prefer auto to explicit type declarations - remember that auto + {expr} => std::initializer_list
- Prefer nullptr to 0 and Null
- Prefer scoped enums to unscoped enums
- Distinguish () and {} when creating objects - the latter disallows narrowing, that might be good
- Declare functions noexcept whenever possible - esp. swap move
- Make const member functions threadsafe - make them bitwise const or internally synchronised
- Distinguish "universal references" from r-value references - "universal references" is his phrase, just note if you see type&&& type might not be an r-value
- Avoid overloading on && - typically r-value ref versus l-value ref is ok, but just r-value ref might be trouble cos it's greedy
- Pass and return r-value refs via std::move, universal refs by std::forward - and allow RVO to happens as before in C++98/03
- Understand reference collapsing See SO
- Assume that move operations are not present, not cheap and not used
- Make std::thread unjoinable on all paths - even if there's an exception
- Use std::launch::async with std::async if asynchronicity is essential - but is it really essential?
- Be aware of varying thread handle destructive behaviour
- Create tasks not threads
- Consider void functions for one-shot event communication
- Pass parameterless functions to std::async, atd::thread}} and {{{std::call_once - the arguments are unconditionally copied, as with std::bind. Use a lambda instead
- Use native handles to transcend the C++11/14 API - if you need to configure your thread, but don't use a thread, so you won't need too
- Prefer lambdas to std::bind - inlining is possible
- Beware default captures in member functions - [=] captures the this pointer, and so member variables via this->variable, which could dangle and are "by ref" i.e. will match [&]. C++14 will add stuff to help
- Use std::make_shared and std::make_unique whenever possible
- Keep abreast of standardisation
Speaking: Running Effective Rehearsals
Pete Goodliffe from Pete Goodliffe
I'll be speaking at The Worship Collective conference in Cambridge, UK on June 29th. This is an awesome event for musicians and worship leaders.I'm leading a seminar entitled Running Effective Rehearsals. Obviously, this is a really practical subject, but I promise it'll be fun too. Hopefully there will be some practical wisdom to apply, and some encouraging advice to take away.