ReadingLab / Discussion-for-Cpp

C++ 中文讨论区
MIT License
89 stars 63 forks source link

Exercise 7.35 问题 #17

Closed csbdong closed 9 years ago

csbdong commented 9 years ago

Exercise 7.35 Explain the following code, indicating which definition of Type or initVal is used for each use of those names. Say how you would fix any errors.

typedef string Type;
typedef string Type;
Type initVal();
class Exercise {
public:
    typedef double Type;
    Type setVal(Type);
    Type initVal();
private:
    int val;
};
Type Exercise::setVal(Type parm) {
    val = parm + initVal();
    return val;
}

有个疑问,为什么Type Exercise::setVal(Type parm) 中第一个Type是指typedef string Type 按照查找的原则不是先在成员函数内查找(此处未找到),然后在类内查找吗?(此处找到)

Ocxs commented 9 years ago

在@pezy 回答之前先说一下自己的理解,讲错了求指教!
其实这句代码是有错的

Type Exercise::setVal(Type parm) {
    val = parm + initVal();
    return val;
}

错误的原因是重载出错(vs2013给的报错解释,它将其理解成重载函数,但是重载函数不能只有返回类型不同,而其他一样 . ) 至于为什么出错,我的理解是:Type Exercise::setVal(Type parm)定义在类外面,而恰好类外面也有Type的声明,而类中的那个TypeType Exercise::setVal(Type parm)不在同一个作用域(相对比来说,可以理解typedef double Type;是在局部作用域,Type Exercise::setVal(Type parm)在全局作用域,其实这样是不对的),所以函数就优先使用类外面的Type,也就是使用了typedef string Type;,但是使用这个的后果就是造成重载出错。

至于你说的那个规则

按照查找的原则不是先在成员函数内查找(此处未找到),然后在类内查找吗?(此处找到)

如果我没理解错的话,可能就是你理解的地方有点小偏差。这里的第一个Type是返回类型,返回类型必须是在使用前确保可见。不会像你说的那样,没找到的情况下继续在类的作用域里寻找。(中文版P254有讲)

Mooophy commented 9 years ago

我的看法是这样:

typedef string Type;
Type initVal();
class Exercise {
public:
    typedef double Type;//此处发生 name hiding,hide了全局的Type。
    Type setVal(Type);
    Type initVal();
private:
    int val;
};

Type Exercise::setVal(Type parm) {
//返回值类型是 string,因为前置的返回值类型不属于class body之内,所以全局Type可见, 而Exercise::Type不可见。
    val = parm + initVal();
    return val;
}

本章节课文的例子和这个基本一样,建议仔细阅读。 @csbdong

Mooophy commented 9 years ago

这个代码我试了三个编译器,vs2013的报错是无法重载。而g++ 和 clang的报错一致,都是函数的声明和定义不匹配。C++不允许class体外的成员函数声明,故可知此处vs2013是出bug了。

应该是前端分析的智能算法什么地方出错了,没理由识别成重载。

以下是三个编译器的报错。 vs2013:

Source.cpp(16) : error C2556: 'Type Exercise::setVal(Exercise::Type)' : overloaded function differs only by return type from 'Exercise::Type Exercise::setVal(Exercise::Type)'
        Source.cpp(11) : see declaration of 'Exercise::setVal'
Source.cpp(16) : error C2371: 'Exercise::setVal' : redefinition; different basic types
        Source.cpp(11) : see declaration of 'Exercise::setVal'

g++ 4.8

error: prototype for 'Type Exercise::setVal(Exercise::Type)' does not match any in class 'Exercise'
 Type Exercise::setVal(Type parm) {
      ^
candidate is: Exercise::Type Exercise::setVal(Exercise::Type)
     Type setVal(Type);
          ^

clang 3.4

error: return type of out-of-line definition of 'Exercise::setVal' differs from that in the declaration
Type Exercise::setVal(Type parm) {
省略若干
csbdong commented 9 years ago

@Mooophy

Type Exercise::setVal(Type parm) {
    val = parm + initVal();
    return val;
}

那么此例中,是不是返回值Type类型和形参的类型Type都是指代的外围的

 typedef string Type

?? 我看编译下来的结果好像形参类型却是类内定义的Type,即Exercise::Type???

//返回值类型是 string,因为前置的返回值类型不属于class body之内,所以全局Type可见, 而Exercise::Type不可见。

Mooophy commented 9 years ago
Type Exercise::setVal(Type parm) {
//class之外--->|<------------ class 之内
    val = parm + initVal();
    return val;
}

所以 该方法的参数是Exercise::Typedouble

csbdong commented 9 years ago

@Mooophy 不太明白,是说前置的返回类型是class body之外的,其余部分属于类内吗???

Mooophy commented 9 years ago

是的

csbdong commented 9 years ago

@Mooophy
@Ocxs 给我的解释中提到: 中文版P254上面这样说:声明中使用的名字,包括返回类型或者参数列表中使用的名字都必须在使用前保证可见。 英文版Name Lookup for Class Member Declarations部分。 那么为什么前置的返回类型是class body之外的,其余部分属于类内呢?? 按照@Ocxs的解释返回类型与形参列表的处理不应该等同吗? 是解释出错了还是我理解错误了? 书上有没有相关的解释呢?

Mooophy commented 9 years ago

@Ocxs 的解释我没太看懂。但这里跟重载没关系,vs很可能是出了个小bug。

我手里没有中文版,谁提供一下对应的英文版章节号。

最重要的是,下面这两句话不矛盾:

1.声明中使用的名字,包括返回类型或者参数列表中使用的名字都必须在使用前保证可见。
2.前置的返回类型是class body之外。
Mooophy commented 9 years ago

仔细理解一下:这两句话的含义和差别。

保证可见 包括但不限于 在class内声明过,还可以是POD,就是int,long之类的内建类型,以及外部声明过的类型比如include进来的,比如std::string,std::vector<int>。只要相应的header被include进来了,那就可见了啊。

csbdong commented 9 years ago

@Mooophy 对应的英文版,是7.4.1节 7.4.1. Name Lookup and Class Scope 第一部分 Name Lookup for Class Member Declarations

csbdong commented 9 years ago

@Mooophy 我之前的疑惑是不明白为什么返回类型是类体之外,而形参列表却在之内,现在在书上找到答案了: 英文版:7.4节7.4. Class Scope 第一小节Scope and Members Defined outside the Class 中提到: 一旦遇到类名,定义的剩余部分就在类的作用于之内了,这里的剩余部分包括参数列表和函数体。

明白了。

csbdong commented 9 years ago

还是看书不仔细。 THX ALL。

Mooophy commented 9 years ago

这种情况下,前置返回值类型就是class之外的,这也是C++11加入了后置返回值的原因。这也是我措辞前置返回值类型,而没说返回值类型的原因。因为后置返回值类型是class之内的。试一下这两段代码: 前置返回值

struct Foo
{
    using T = int;
    T bar(int, long );
};

T Foo::bar(int i, long j) 
{
    return 42;
}

int main()
{

}

后置返回值

struct Foo
{
    using T = int;
    T bar(int, long );
};

auto Foo::bar(int i, long j) -> T 
{
    return 42;
}

int main()
{

}
Mooophy commented 9 years ago

前者无法编译,后者正常编译。 以后多写template就熟悉了。

csbdong commented 9 years ago

3Q