Sunday, June 4, 2017

Digest for comp.lang.c++@googlegroups.com - 2 updates in 1 topic

Christiano <christiano@engineer.com>: Jun 04 06:39AM -0300

There is a bug in the book PPP2[1] that made me wonder what would be
the reasons for C ++ to accept X{1,2,3,4} to be the same thing as X{{1,2,3,4}}.
 
The bug is:
"The string{1,c} is a constructor for string, initializing the string to contain the single character c."
 
No. The string{1,'a'} will create a vector with two elements: 1 and 97 ('a' ascii value).
The correct should be string(1,c).
 
 
Proof:
------------begin proof------------------------
#include <iostream>
#include <string>
using namespace std;
 
void f(const string &s, const string &label)
{
cout << "=== String \"" << label << "\" ===" << endl;
cout << "size: " << s.size() << endl;
for(const char &c : s)
cout << int(c) << endl;
}
 
int main()
{
f(string{1,'a'}, "1");
f(string(1,'a'), "2");
 
return 0;
}
----------- end proof------------------------
 
Output:
 
=== String "1" ===
size: 2
1
97
=== String "2" ===
size: 1
97
 
Ok.
 
Now, look this program:
 
-----------test.cpp begin-----------------------
#include <iostream>
#include <initializer_list>
using namespace std;
 
class car
{
public:
car(int n)
{
cout << "Constructor int" << endl;
}
car(initializer_list<int> lst)
{
cout << "Constructor initializer_list" << endl;
for(auto &x: lst)
cout << x << " ";
cout << endl;
}
};
 
int main()
{
car v{1};
car u{{1,2,3}};
car t{1,2,3};
// car r{{{1,2,3}}}; ERROR
return 0;
}
-------------test.cpp end----------------------
 
 
The output is:
Constructor initializer_list
1
Constructor initializer_list
1 2 3
Constructor initializer_list
1 2 3
 
That is: v, u and t are being constructed using car(initializer_list<int> lst).
v using car(initializer_list<int> lst)
u using car(initializer_list<int> lst)
t using car(initializer_list<int> lst)
 
What I want to discuss here is: Wouldn't a result similar to following be more reasonable?:
 
v using car(int n)
u using car(initializer_list<int> lst)
t using car(int, int, int) ---> ERROR because you haven't defined car(int, int, int) here
 
My point here is: The compiler forbidding X{1,2,3,4} being equal to X{{1,2,3,4}}:
 
1- would be orthogonally compatible with the error of writing r{{{1,2,3}}}
2- Prevents mixing of C++98 syntax (x(1)) with C++11 syntax (x{1})
 
Is there any special reason to X{1,2,3,4} being equal to X{{1,2,3,4}} in spite of the benefits that language would gain without it??
 
----
[1] PPP2 book : http://www.stroustrup.com/Programming/
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 04 12:33PM +0200

On 04-Jun-17 11:39 AM, Christiano wrote:
 
> No. The string{1,'a'} will create a vector with two elements: 1 and 97
> ('a' ascii value).
> The correct should be string(1,c).
 
Right, except formally the presumption of ASCII.
 
 
> Proof:
 
Careful. The result from a given implementation is not proof of
anything. It can support an argument and serve as an heuristic.
 
The final authority on C++ is the C++ standard.
 
But in this case the behavior is well known so you don't need to supply
a proof: you only need to point out the error. For example, the
idiomatic way to create a single character string is `string{c}`, which
relies on the same precedence for initalizer list overload, and which
doesn't work with round parenthesis. At a guess Bjarne originally wanted
the curly braces syntax to be a drop-in replacement for the original
round parenthesis syntax, but it didn't work out that way.
 
 
> X{{1,2,3,4}}:
 
> 1- would be orthogonally compatible with the error of writing r{{{1,2,3}}}
> 2- Prevents mixing of C++98 syntax (x(1)) with C++11 syntax (x{1})
 
As I recall in the original C syntax internal braces in an initializer
were optional. Optional is a one-way thing. You can omit but you can
generally not add.
 
 
> Is there any special reason to X{1,2,3,4} being equal to X{{1,2,3,4}} in
> spite of the benefits that language would gain without it??
 
E.g. `std::array`, or more precisely the original `boost::array`, since
the standard library can any kind of magic, relies on this.
 
The issues we have now with that syntax were not possible, and hence not
considered, in early C, or for that matter in modern C.
 
When you design a new class it's easy to avoid the problem by e.g.
adding a tag argument to the value list constructor:
 
#include <iostream>
#include <initializer_list>
using namespace std;
 
namespace with {
struct Values {} const values = {};
} // namespace with
 
class Car
{
public:
Car( int )
{
cout << "<init>( int )" << endl;
}
 
Car( with::Values, initializer_list<int> const values )
{
cout << "<init>( initializer_list ) " ;
for( auto const x : values ) { cout << x << " "; }
cout << endl;
}
};
 
auto main()
-> int
{
Car v{ 1 }; // int
Car u{ with::values, {1,2,3} }; // values
// Car t{1,2,3}; //! error
// Car r{{{1,2,3}}}; //! error
}
 
Cheers & hth.,
 
- Alf
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: