This is indeed valid code and like the rest of the answers I will warn you that this should be used very carefully since it can be confusing and would probably lead to hard to maintain code.
So why does this work? If we consider your constructor:
Struct(const T& value) : value(value) {}
^ ^
1 2
1
and 2
are evaluated in different scopes. So we need to look at the draft C++ standard section 12.6.2
Initializing bases and members and look at some grammar:
ctor-initializer:
: mem-initializer-list
mem-initializer-list:
mem-initializer ...opt
mem-initializer , mem-initializer-list ...opt
mem-initializer:
mem-initializer-id ( expression-listopt )
mem-initializer-id braced-init-list
After digesting this we see that 1
is really a mem-initializer-id and 2
is a expression-listopt and we can go to paragraph 2 and 12 respectively for each of these. Paragraph 2 says:
In a mem-initializer-id an initial unqualified identifier is looked up in the scope of the constructor’s class and, if not found in that scope, it is looked up in the scope containing the constructor’s definition. [...]
So 1
will be looked up in the class first while we can see from paragraph 12 which says:
Names in the expression-list or braced-init-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
2
will be looked up in the scope of the constructor. So 1
will find the member variable first and stop looking while 2
will look in the constructor and find the parameter. This also means that if you wanted to refer to a member variable in an expression-list you would have to use this->.