Pregunta

Is this valid C++, assuming I want to copy the argument variable to the member variable:

struct Struct {
  Struct(const T& value) : value(value) {}
  T value;
};

(Update: It works in Visual Studio, but still that might be compiler dependent) (Expected question: Why do you need this? Answer: Macro-making purposes)

¿Fue útil?

Solución

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->.

Otros consejos

It is perfectly valid, standard-conformant code, beyond any doubt.

Struct(const T& value) : value(value) {}
                               ^^^^^ this is argument
                         ^^^^^ this is the member

Now the question is: is this good practice? In my opinion, NO. I prefer my data-members to follow different yet consistent naming convention, such as data-members always start with _. So I would prefer this:

Struct(const T& value) : _value(value) {}

where _value is data member. You could follow any naming convention — just make sure that you're consistent.

Note that in your code variable, functions, classes or any identifier should not start with double underscore such as __value, or single underscore followed by upper case letter such as _Value — such names are reserved for implementations.

This is permitted by the C++ standard, but consider the case where, after initializing members, you want to do more work in the function. For example, using 3 to stand in for some more meaningful calculation:

class Foo
{
public:
    int bar;
    Foo(int bar) : bar(bar) { bar = 3; }
};

The assignment in the function changes the value of the parameter bar, not the value of the member bar. This cannot happen in your example, because you declared the parameter with const. So, if you are sure to always declare parameters with const, you are protected from this. But consider a more complicated scenario:

class Foo
{
public:
    int bar;
    int baz;
    void AuxiliaryFunction() { bar = 3; }
    Foo(const int &bar) : bar(bar)
    {
        AuxiliaryFunction();
        baz = bar;
    }
};

In this example, the member bar is given some value through another function called in the constructor. Then the assignment baz = bar; may be intended to copy the member bar, but it actually copies the parameter bar.

So, while this is legal C++, it should be used judiciously.

Yes. It does compile. For the compiler, there is no ambiguity of which value is which.

#include <iostream>

using namespace std;

template <typename T>
struct Struct {
    Struct(const T & value) : value(value) {}
    T value;
};

int main() {

    Struct<int> T(1);
    // your code goes here
    return 0;
}

http://ideone.com/gPyBK6

But at stated multiple times, this is not easy for the programmer to decipher. It does work because for the compiler the parameter mask the member function, so the second value in value(value) is the parameter, but as only member and ancestor class can be on the left part of value(value), it does refer to the member here.

Tricky enough to complicate debugging and maintenance.

It is valid, but ill-advised in some circles, including my circle.

It is valid in the sense that the member variable will be properly set by the parameter as you desire. Following the execution of the initializer list, the member will be hidden. Any reference to value will access the parameter. This is likely a bad thing.

It is ill-advised for two reasons. First and foremost, maintainability & confusion. It is unusual to see a parameter and member variable have the same name. Because of this, most programmers will have to stop and think about what it means. After all, you did. Remember that code is written first for programmers, second for compilers. Code that is easy to understand is much better than code that is hard to understand. In a code review I would reject this code on these grounds.

Second, the member hiding will likely be a problem in most scenarios.

I'd suggest coming up with a sane naming scheme and sticking with it. "Sane" means a parameter can never have the same name as a member variable. For example in my naming scheme, member variables are always prepended with m -- parameters never prepended. So in this scheme your code would become:

struct Struct {
  Struct(const T& value) : mValue(value) {}
  T mValue;
};

Using this scheme, nobody is confused about what's happening here and nobody has to ask StackOverflow "is this legit?"

It is valid. However a little warning: Changing the argument name and the code is undefined behavior.

template <typename T>
struct Struct {
    Struct(const T & argument) : value(value) {}
    T value;
};

It's valid but, as evidenced by the need to even ask this, potentially jarring and confusing.

The perennial problem is coming up with two distinct names for what is kinda the same thing.

I like to do this (but some people hate it):

struct Struct {
  Struct(const T& newValue) : value(newValue) {}
  T value;
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top