I have some rather odd behavior in my D program that I've narrowed down to this:
import std.algorithm;
import std.stdio;
import std.traits;
enum E { a, b, c };
struct S { E e; };
void main()
{
immutable(S)[] source = [ S(E.a), S(E.a), S(E.b) ];
foreach (e; EnumMembers!E)
{
size_t c = count!(x => x.e == e)(source);
writeln(e, " -> ", c);
}
}
I would expect the output of this program to be something along the lines of:
a -> 2
b -> 1
c -> 0
But the actual result is:
a -> 2
b -> 2
c -> 2
Curiously, changing the for loop to foreach (e; [ E.a, E.b, E.c ])
produces my expected output. Using foreach (e; [ EnumMembers!E ])
also produces my expected result, so clearly my use of the range from EnumMemebers
is the problem here...I just don't know why.
I am clearly doing something wrong, but I have no idea what and would appreciate some insight.
My compiler is DMD64 D Compiler v2.059
on Linux.
EDIT: This has the exact same behavior with GDC 4.6.3, so it can't be a compiler bug.
EDIT: By moving the count
call to a separate function:
size_t counte(Range)(E e, Range src)
{
return count!(x => x.e == e)(src);
}
and changing c
's initialization to size_t c = counte(e, source);
, the program works as I would expect.
Short and incomplete answer that may help with direction to dig for:
EnumMembers!T is a type tuple ( http://dlang.org/tuple.html -> Type Tuple ), not an expression tuple or array. When you use [ EnumMembers!T ] syntax, tuple is used as an initializer list at compile time and regular array is created, providing expected behavior.
Now, if you use type tuple in a foreach statement as-is, things gotta get interesting. There is a special case foreach for tuples: http://dlang.org/statement.html -> Foreach over Tuples. And in case of expression type tuples ( sorry for weird wording, but unfortunately, that is how it is named in D ) it does not really create a variable - it just replaces all usages of e with expression taken from type tuple.
And here we go - e in lambda is just replaced with expression from tuple and so this lambda is NOT a delegated. I have checked it's typeof, it is "bool function(S x) pure nothrow". Guess it is created the very first time it is used, remembering e expression in lambda code and then just using it as-is.
I need someone else to comment about is it a bug, misfeature or works as intended.