Sunday, November 27, 2016

The Great Foreign Edition Book Giveaway

One of the nicer author perks is seeing your books appear in translation. In my 2003 Advice to Prospective Book Authors, I wrote:
Few things evoke quite the level of giddiness as seeing a copy of your book in a foreign script. I, for one, cherished my books in Chinese, and I continued to cherish them even after I found out that they were actually in Korean.
My publishers generally send me at least one copy of each translation they authorize. I often receive several copies, however, and over the years, I've amassed  more copies of my books in foreign languages than I have use for. Look!—these are the extra copies I currently have:

Instead of letting these books gather more dust, I've decided to give them away. Want one? Just ask. I'll autograph it for you and throw it in the mail, and all I'll request in return is that you cover the cost of postage.

I'll describe the details of how the giveaway works in a moment, but first let me show you the available inventory. Most books are in a language other than English, but what I'm technically giving away are foreign editions, so a few have the same text as the US book (i.e., they're in English). Such editions are generally printed on cheaper paper than their US counterparts, and like almost all the books I'm giving away, they use only one ink color, even if the US version uses multiple colors.

Here's what I've got:

Things to bear in mind:

  • For books with two ISBN lines, each line represents a distinct ISBN for the book. The upper one is the older ISBN-10. The lower one is the newer ISBN-13. (ISBN-10 vs ISBN-13 is the publishing equivalent of IPv4 vs. IPv6.)
  • Sometimes there are multiple versions of the same translation, e.g., there are two entries for German and for Japanese translations of Effective C++, Third Edition. In such cases, the only difference is typically the cover design. As far as I know, the substance of all translations of a particular book into a particular language is the same.
  • In the table, "Chinese" is ambiguous, because there are two versions of printed Chinese: traditional and simplified. To find out which Chinese is meant, use your favorite search engine to look up a book's ISBN.
  • I've tried to list accurate languages for the books, but, not being able to read most of them, I may have made a mistake here and there. If so, I apologize, and I hope you'll bring the errors to my attention.
  • The first two editions of Effective C++ are either old or really old. Both are out of date. They might be suitable for a C++ museum, or maybe you could employ them as research material for that Scott Meyers biography you've been working on (ahem), but the programming advice in these editions is not to be trusted. I'll send them to you if you ask me to, but before you make a request, think carefully about why you're doing it. It shouldn't be to improve your C++.

How the giveaway works:

  • If you'd like a book, send me email letting me know what you want and the address to which I should send it. If you'd like more than one book, that's fine, just list the books in priority order. (I'll ignore book requests posted as comments to this blog, sorry.)
  • I'll let the requests roll in for about two weeks (until about December 9), then I'll decide who gets what on whatever basis I want. My general plan is to assign higher priority to earlier requests and to issue everybody one book before issuing anybody more than one (i.e., to use a pseudo-FIFO pseudo-round-robin algorithm), but my plan might change. If your request includes an unusually good reason to satisfy it, I'll increase your priority. (An example of an unusually good reason would be that you'd like books to stock a library, thus making them available to many people.)
  • At some point (by December 16, I hope), I'll let you know whether I can satisfy your request. If I can, I'll put your book(s) in the mail, let you know how much the postage is, and request that you send me that much by Paypal. As it happens, I've gone down this road a couple of times in the past, and some of the promised payments never materialized. Nevertheless, my faith in the basic honesty of C++ software developers endures. I'd appreciate it if you wouldn't do anything to change that.
Soooo...who wants a book that I can't read, that's out of date, or both?

Scott

Monday, November 21, 2016

Help me sort out the meaning of "{}" as a constructor argument

In Effective Modern C++, one of the explanations I have in Item 7 ("Distinguish between () and {} when creating objects") is this:
If you want to call a std::initializer_list constructor with an empty std::initializer_list, you do it by making the empty braces a constructor argument—by putting the empty braces inside the parentheses or braces demarcating what you’re passing:
  
class Widget {
public:
  Widget();                                   // default ctor
  Widget(std::initializer_list<int> il);      // std::initializer_list ctor
  …                                           // no implicit conversion funcs
}; 

Widget w1;          // calls default ctor
Widget w2{};        // also calls default ctor
Widget w3();        // most vexing parse! declares a function!    

Widget w4({});      // calls std::initializer_list ctor with empty list
Widget w5{{}};      // ditto  
I recently got a bug report from Calum Laing saying that in his experience, the initializations of w4 and w5 aren't equivalent, because while w4 behaves as my comment indicates, the initialization of w5 takes place with a std::initializer_list with one element, not zero.

A little playing around showed that he was right, but further playing around showed that changing the example in small ways changed its behavior. In my pre-retirement-from-C++ days, that'd have been my cue to dive into the Standard to figure out what behavior was correct and, more importantly, why, but now that I'm supposed to be kicking back on tropical islands and downing piƱa coladas by the bucket (a scenario that would be more plausible if I laid around on beaches...or drank), I decided to stop my research at the point where things got complicated. "Use the force of the Internet!," I told myself. In that spirit, let me show you what I've got in the hope that you can tell me why I'm getting it. (Maybe it's obvious. I really haven't thought a lot about C++ since the end of last year.)

My experiments showed that one factor affecting whether "{{}}" as an argument list yields a zero-length std::initializer_list<T> was whether T had a default constructor, so I threw together some test code involving three classes, two of which could not be default-constructed. I then used both "({})" (note the outer parentheses) and "{{}}" as argument lists to a constructor taking a std::initializer_list for a template class imaginatively named X. When the constructor runs, it displays the number of elements in its std::initializer_list parameter.

Here's the code, where the comments in main show the results I got under all of gcc, clang, and vc++ at rextester.com.  Only one set of results is shown, because all three compilers produced the same output.
  
#include <iostream>
#include <initializer_list>

class DefCtor {
public:
  DefCtor(){}
};

class DeletedDefCtor {
public:
  DeletedDefCtor() = delete;
};

class NoDefCtor {
public:
  NoDefCtor(int){}
};

template<typename T>
class X {
public:
  X() { std::cout << "Def Ctor\n"; }
    
  X(std::initializer_list<T> il)
  {
    std::cout << "il.size() = " << il.size() << '\n';
  }
};

int main()
{
  X<DefCtor> a0({});           // il.size = 0
  X<DefCtor> b0{{}};           // il.size = 1
    
  X<DeletedDefCtor> a2({});    // il.size = 0
  X<DeletedDefCtor> b2{{}};    // il.size = 1

  X<NoDefCtor> a1({});         // il.size = 0
  X<NoDefCtor> b1{{}};         // il.size = 0
}
These results raise two questions:
  1. Why does the argument list syntax "{{}}" yield a one-element std::initializer_list for a type with a default constructor, but a zero-element std::initializer_list for a type with no default constructor?
  2. Why does a type with a deleted default constructor behave like a type with a default constructor instead of like a type with no default constructor?
If I change the example to declare DefCtor's constructor explicit, clang and vc++ produce code that yields a zero-length std::initializer_list, regardless of which argument list syntax is used:
class DefCtor {
public:
  explicit DefCtor(){}             // now explicit
};

...

X<DefCtor> a0({});           // il.size = 0
X<DefCtor> b0{{}};           // il.size = 0 (for clang and vc++)  
However, gcc rejects the code:
source_file.cpp:35:19: error: converting to ‘DefCtor’ from initializer list would use explicit constructor ‘DefCtor::DefCtor()’
   X<DefCtor> b0{{}};
                   ^
gcc's error message suggests that it may be trying to construct a DefCtor from an empty std::initializer_list in order to move-construct the resulting temporary into b0. If that's what it's trying to do, and if that's what compilers are supposed to do, the example would become more complicated, because it would mean that what I meant to be a series of single constructor calls may in fact include calls that create temporaries that are then used for move-constructions.

We thus have two new questions:
  1. Is the code valid if DefCtor's constructor is explicit?
  2. If so (i.e., if clang and vc++ are correct and gcc is incorrect), why does an explicit constructor behave differently from a non-explicit constructor in this example? The constructor we're dealing with doesn't take any arguments.
The natural next step would be to see what happens when we declare the constructors in DeletedDefCtor and/or NoDefCtor explicit, but my guess is that once we understand the answers to questions 1-4, we'll know enough to be able to anticipate (and verify) what would happen. I hereby open the floor to explanations of what's happening such that we can answer the questions I've posed. Please post your explanations in the comments!

---------- UPDATE ----------

As several commenters pointed out, in my code above, DeletedDefCtor is an aggregate, which is not what I intended. Here's revised code that eliminates that. With this revised code, all three compilers yield the same behavior, which, as noted in the comment in main below, includes failing to compile the initialization for b2. (Incidentally, I apologize for the 0-2-1 ordering of the variable names. They were originally in a different order, but I moved them around to make the example clearer, then forgot to rename them, thus rendering the example probably more confusing, sigh.)
  
#include <iostream>
#include <initializer_list>
 
class DefCtor {
  int x;
public:
  DefCtor(){}
};
 
class DeletedDefCtor {
  int x;
public:
  DeletedDefCtor() = delete;
};
 
class NoDefCtor {
  int x;    
public:
  NoDefCtor(int){}
};
 
template<typename T>
class X {
public:
  X() { std::cout << "Def Ctor\n"; }
     
  X(std::initializer_list<T> il)
  {
    std::cout << "il.size() = " << il.size() << '\n';
  }
};
 
int main()
{
  X<DefCtor> a0({});           // il.size = 0
  X<DefCtor> b0{{}};           // il.size = 1
     
  X<DeletedDefCtor> a2({});    // il.size = 0
  // X<DeletedDefCtor> b2{{}};    // error! attempt to use deleted constructor
 
  X<NoDefCtor> a1({});         // il.size = 0
  X<NoDefCtor> b1{{}};         // il.size = 0
}
This revised code renders question 2 moot.

The revised code exhibits the same behavior as the original code when DefCtor's constructor is declared explicit: gcc rejects the initialization of b0, but clang and vc++ accept it and, when the code is run, il.size() produces 0 (instead of the 1 that's produced when the constructor is not explicit).

---------- RESOLUTION ----------

Francisco Lopes, the first person to post comments on this blog post, described exactly what was happening as regards questions 1 and 2 about the original code I posted. The only thing he didn't do was cite sections of the Standard, which I can hardly fault him for. From my perspective, the key provisions in the C++14 Standard are
  • 13.3.1.7 ([over.match.list]), which says that when you have a braced initializer for an object, you first try to treat the entire initializer as an argument to a constructor taking a std::initializer_list. If that doesn't yield a valid call, you fall back on viewing the contents of the braced initializer as constructor arguments and perform overload resolution again.
and
  • 8.5.4/5 ([dcl.init.list]/5), which says that if you're initializing a std::initializer_list from a braced initializer, you copy-initialize each element of the std::initializer_list from the corresponding element of the braced initializer. The relevance of this part of the Standard was brought to my attention by Marco Alesiani in his comment below.
The behavior of the initializations of a0 and b0, then, can be explained as follows:
  
X<DefCtor> a0({});  // The arg list uses parens, not braces, so the only ctor argument is
                    // "{}", which, per 13.3.3.1.5/2 ([over.ics.list]/2) becomes an empty
                    // std::initializer_list. (Thanks to tcanens at reddit for the 
                    // reference to 13.3.3.1.5.)

X<DefCtor> b0{{}};  // The arg list uses braces, so the ctor argument is "{{}}", which is
                    // an initializer list with one element, "{}". DefCtor can be
                    // copy-initialized from "{}", so the ctor's std::initializer_list
                    // param contains a single default-constructed DefCtor object.
I thus understand the error in Effective Modern C++ that Calum Laing brought to my attention. The information in the comments (and in this reddit subthread) regarding how explicit constructors affect things is just a bonus.

Thanks to everybody for helping me understand what was going on. All I have to do now is figure out how to use this newfound understanding to fix the problem in the book...

Wednesday, November 9, 2016

Test Post -- Please Ignore

This is test content. Please ignore.