Three supposedly cool things about D

S

SG

In case you havn't heard about Alexandrescu's Google TechTalk "Three
Cool Things about D", Here are a couple of links:

(video of the talk)
http://erdani.com/d/three-cool-things-about-d.pdf (slides)

Since he's arguing for D by comparing it to C++, I thought this could
be of interest to the readers here.

I myself don't find all the arguments particularly compelling. For
example, he's arguing that D has a better support for generic
programming. He does that with an example: a generic min function.

auto min(L,R)(L lhs, R rhs) // this is D2, not C++
{
return rhs < lhs ? rhs : lhs;
}

This is supposedly all it takes for a "complete min implementation".
The problem here is IMHO: What is a "complete min implementation"
actually supposed to do? The different C++ implementations he mentions
(including the 175 lines code from N2199) obviously have different
goals. At least two of the goals of N2199 are not met by the D
implementation from above. That is, it won't return references when it
is safe (i.e. when both arguments were reference-compatible lvalues)
and it will return the wrong results due to implicit conversions
involving signed/unsigned types. So, in my opinion, this is a bit of
an apples-oranges comparison. A nice contender for his min
implementation in D would probably look like this:

template<class T, class U>
auto min(T && t, U && u) -> decltype(u<t?u:t)
{
if (u<t) return forward<U>(u);
return forward<T>(t);
}

which does all that the D implementation from above does and more
(return values are possibly move-constructed --> no unnecessary
copying, support for move-only types. It does return an lvalue
reference in case both arguments were reference-compatible lvalues).
It doesn't solve the signed/unsigned problem but his D version is just
as flawed in that respect.

Any other thoughts?

Cheers!
SG
 
J

Juha Nieminen

SG said:
auto min(T && t, U && u) -> decltype(u<t?u:t)

I must admit that I haven't studied C++0x all that much, but I can't
understand how that decltype can work. The expression 'u<t?u:t' as an
ambiguous type if 'u' and 't' are of different types and, AFAIK, decltype
is a compile-time expression, not a runtime one, and hence the type of
the expression cannot be defined if it's ambiguous in that way.

(Moreover, I think that creating a generic min() function which accepts
parameters of different types is quite dubious design (because the return
value becomes ambiguous), so the whole example seems a bit bogus.)
 
S

Stefan van Kessel

I must admit that I haven't studied C++0x all that much, but I can't
understand how that decltype can work. The expression 'u<t?u:t' as an
ambiguous type if 'u' and 't' are of different types and, AFAIK, decltype
is a compile-time expression, not a runtime one, and hence the type of
the expression cannot be defined if it's ambiguous in that way.


While decltype indeed is a compile-time expression, the type of
'u<t?u:t' does _not_ depend on whether u<t. The type of a
conditional-expression is fixed at compile time following several rules
outlined in 5.16 in both the current standard and the current working
draft. If U and T are different, then either u will be converted to T
whenever it is smaller or t will be converted to U whenever it is
smaller. If both conversions are equally suitable (for "standardese"
look at 5.16), the program is ill-formed.

So using "auto min(T&& t, U&& u) -> decltype(u<t?u:t)" can be
reasonable suitable in some probably rare cases (e.g. when one actually
does want the typical type promotion of built in numerical types) but it
does not do what one might expect.

have a nice day,
Stefan
 
S

SG

  I must admit that I haven't studied C++0x all that much, but I can't
understand how that decltype can work. The expression 'u<t?u:t' as an
ambiguous type if 'u' and 't' are of different types and,

The ?: operator is quite flexible/powerful (more on that later).

Though, I made a mistake here. Since t and u are named references,
they are always treated as lvalues. This is not what I wanted. The
following version is the right one:

template<class T, class U>
auto min(T && t, U && u)
-> decltype( u<t ? declval<U>() : declval<T>() )

Here, declval<T>() is an lvalue expression if and only if T is an
lvalue reference. And if the first argument to this function template
really was an lvalue expression the type parameter T will be deduced
to be an lvalue reference due to the rules that allow perfect
forwarding. This is near-equivalent to std::common_type<T,U>::type as
the return type except that the less than comparison between u and t
helps in "constraining" the function template (a la SFINAE). So, if u
and t are not less than comparable this function template is simply
going to be ignored.

Coming back to the operator ?:, as I said. It's quite powerful/
flexible. Here are a couple of examples of how it behaves for various
type parameters T and U:

std::common_type<T,U>::type =
T U decltype(true?declval<T>():declval<U>())
--------------------------------------
int& int& int& (retains lvalueness in this case)
int& int const& int const& (still an lvalue reference)
int int const& int (one operand was an rvalue)
int double double
int& double& double (types are not reference-related)
Base& Derived& Base& (Derived& to Base& conversion)
AFAIK, decltype is a compile-time expression, not a runtime one,

It's a compile-time operator.
and hence the type of
the expression cannot be defined if it's ambiguous in that way.

In case the types T and U are not compatible in terms of this decltype
expression from above this counts as a template argument deduction
failure and the compiler silently ignores the template (substitution
failure is not an error). Of course, in case there is no other viable
candidate, you'll get a compile-time error saying that there is no
such function that can be called like this (or something like that).
  (Moreover, I think that creating a generic min() function which accepts
parameters of different types is quite dubious design (because the return
value becomes ambiguous), so the whole example seems a bit bogus.)

Well, it is nice to have min(a,b) work in case the types don't match
perfectly. For example, min(myvector.size(),44) should work, shouldn't
it? The second argument is an int, but the first argument is some
unsigned type. This is a case that std::min can't handle because it is
only parameterized with one type parameter (--> deduction failure).
But now, with C++0x features, we're able to handle this without having
to write tons of traits classes, specializations, etc.

Cheers!
SG
 
S

SG

[...] AFAIK, decltype is a compile-time expression
not a runtime one, [...]

While decltype indeed is a compile-time expression, the type of
'u<t?u:t' does _not_ depend on whether u<t.

Right. Nice catch. It didn't occur to me that Juha might have assumed
the type of x?y:z to depend on x.

Cheers!
SG
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top