Templated struct doesn't need the parameterized type in return type definitions?

Steven Schveighoffer schveiguy at yahoo.com
Tue Mar 8 12:25:27 PST 2011


On Tue, 08 Mar 2011 14:20:40 -0500, spir <denis.spir at gmail.com> wrote:

> On 03/08/2011 06:20 PM, Steven Schveighoffer wrote:
>> On Tue, 08 Mar 2011 12:06:08 -0500, Andrej Mitrovic <none at none.none>  
>> 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


More information about the Digitalmars-d-learn mailing list