On Tue, 08 Mar 2011 14:20:40 -0500, spir <[email protected]> wrote:
On 03/08/2011 06:20 PM, Steven Schveighoffer wrote:
On Tue, 08 Mar 2011 12:06:08 -0500, Andrej Mitrovic <[email protected]>
wrote:
import std.stdio;
import std.traits;
import std.exception;
struct CheckedInt(N) if (isIntegral!N)
{
private N value;
ref CheckedInt opUnary(string op)() if (op == "++")
{
enforce(value != value.max);
++value;
return this;
}
this(N _value)
{
value = _value;
}
}
I didn't know you could define a return type of a templated struct
without
defining the type it is parameterized on. I mean this line:
ref CheckedInt opUnary(string op)() if (op == "++")
I thought for sure I always had to write the parameterized type like
so:
ref CheckedInt!(N) opUnary(string op)() if (op == "++")
So I guess this really isn't a question but more of a "oh, I didn't
know you
could do that". In fact I rarely see this kind of code in Phobos, most
of the
time the parameterized type is specified in these types of cases. Is
this
feature described somewhere, because I must have missed it if it is?
It is described, but not directly.
Look on this page:
http://www.digitalmars.com/d/2.0/template.html
From there we have these two descriptions:
------------------------
If a template has exactly one member in it, and the name of that member
is
the same as the template name, that member is assumed to be referred to
in
a template instantiation:
template Foo(T)
{
T Foo; // declare variable Foo of type T
}
void test()
{
Foo!(int) = 6; // instead of Foo!(int).Foo
}
------------------------
If a template declares exactly one member, and that member is a class
with
the same name as the template:
template Bar(T)
{
class Bar
{
T member;
}
}
then the semantic equivalent, called a ClassTemplateDeclaration can be
written as:
class Bar(T)
{
T member;
}
------------------------
Also note that structs have the same description.
So if you think about it, your code is equivalent to:
template CheckedInt(N) if(isIntegral!N)
{
struct CheckedInt
{
...
}
}
If you look at it this way, it makes complete sense that within the
struct
that's within the template, the struct can refer to itself without the
specific
instantiation parameters.
I think this should really be laid out properly in the docs. I
discovered this
"trick" while writing dcollections by accident and thought it so
awesome that I
changed all my code which self-returned (quite a bit).
-Steve
I don't share your enthusiasm, Steven, for this feature (which I did not
know). In fact, I tend to consider it a mis-feature. Yet another
syntactic special-case for special cases in the language. In this case,
there are even 3 ways to write the same thing:
CheckedInt
CheckedInt!N
CheckedInt!(N)
And note these variants are low-level ones, morphological rather than
syntactic properly speaking.
Here's another thing I found in dcollections which caught me off guard,
and which I was glad to be rid of when I switched to not parameterizing
the names of self returns:
class Collection(T)
{
Collection!(T) add(T t) { ...; return this; }
// 20 other functions like add...
}
"Hey, wouldn't it be cool if I could add a custom allocator to all
classes!?"...
class Collection(T, alloc = DefaultAllocator!T)
{
Collection!(T) add(T t) { ...; return this; }
// 20 other now subtly incorrect functions like add...
}
See the problem?
-Steve