使用相同的参数名称和成员名称有效

时间:2013-12-16 15:57:56

标签: c++ variables constructor language-lawyer argument-passing

这是有效的 C ++ ,假设我想将参数变量复制到成员变量:

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

(更新:它在Visual Studio中有效,但仍然可能依赖于编译器) (预期问题:为什么需要这个?答案:制作宏观目的

6 个答案:

答案 0 :(得分:3)

这确实是有效的代码,就像其他答案一样,我会警告你应该非常小心地使用它,因为它可能会让人感到困惑,并可能导致很难维护代码。

那么为什么这样呢?如果我们考虑你的构造函数:

Struct(const T& value) : value(value) {}
                         ^     ^
                         1     2    

12在不同的范围内进行评估。因此,我们需要查看draft C++ standard部分12.6.2 初始化基础和成员并查看一些语法:

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

消化后,我们发现1实际上是 mem-initializer-id ,而2 expression-listopt ,我们可以分别转到段落 2 12 。段落 2 说:

  

在mem-initializer-id中,在构造函数的类的范围内查找初始的非限定标识符,如果在该范围内找不到,则在包含构造函数定义的范围中查找它。 [...]

首先我们会在课堂上查找1,而我们可以从段落 12 中看到:

  

mem-initializer的expression-list或braced-init-list中的名称在指定了mem-initializer的构造函数的范围内进行计算。

2将在构造函数的范围内查找。因此1将首先找到成员变量并停止查找2将在构造函数中查找并找到参数。这也意味着如果您想在表达式列表中引用成员变量,则必须使用 this->

答案 1 :(得分:2)

是。它确实编译。对于编译器,没有任何歧义value是哪个。

#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

但是多次声明,程序员破译并不容易。它确实有效,因为对于编译器,参数掩盖了成员函数,因此value中的第二个value(value)是参数,但因为只有成员和祖先类可以位于{{1}的左侧部分},它确实引用了这里的成员。

足以使调试和维护变得复杂。

答案 2 :(得分:2)

这是有效的,但在某些圈子中是不明智的,包括我的圈子。

在某种意义上它是有效的,成员变量将根据您的需要由参数正确设置。执行初始化程序列表后,将隐藏该成员。对value的任何引用都将访问该参数。这可能是一件坏事。

出于两个原因,这是不明智的。首先,可维护性和混乱。看到参数和成员变量具有相同的名称是不常见的。因此,大多数程序员必须停下来并思考这意味着什么。毕竟,你做到了。请记住,代码首先为程序员编写,其次是编译器。易于理解的代码比难以理解的代码要好得多。在代码审查中,我会基于这些理由拒绝此代码。

其次,在大多数情况下,成员隐藏可能会成为一个问题。

我建议提出一个理智的命名方案并坚持下去。 “Sane”表示参数永远不能与成员变量同名。例如,在我的命名方案中,成员变量总是以m为前缀 - 参数永远不会被添加。所以在这个方案中你的代码将成为:

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

使用这个方案,没有人对这里发生的事情感到困惑,没有人要问StackOverflow“这是合法的吗?”

答案 3 :(得分:2)

这是C ++标准允许的,但考虑在初始化成员之后,您希望在函数中执行更多工作的情况。例如,使用3代表更有意义的计算:

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

函数中的赋值会更改参数bar的值,而不会更改成员bar的值。在您的示例中不会发生这种情况,因为您使用const声明了参数。因此,如果您确定始终使用const声明参数,则会受到保护。但考虑一个更复杂的情况:

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

在此示例中,成员bar通过构造函数中调用的另一个函数获得一些值。然后,作业baz = bar;可能会复制成员bar,但它实际上会复制参数bar

所以,虽然这是合法的C ++,但应该明智地使用它。

答案 4 :(得分:1)

有效。但是有一点警告:更改参数名称和代码是未定义的行为。

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

答案 5 :(得分:0)

这是有效的,但正如需要提出这一点所证明的那样,可能会引起混乱和混乱。

长期存在的问题是出现了两个截然不同的名称。

我喜欢这样做(但有些人讨厌它):

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