为什么max(max(a,b), c)会出错?

4

参考资料:来自C++模板编程指南的代码片段

// maximum of two values of any type (call-by-reference) 
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
} 

// maximum of two C-strings (call-by-value) 
inline char const* max (char const* a, char const* b) 
{ 
    return std::strcmp(a,b) < 0 ? b : a; 
} 

// maximum of three values of any type (call-by-reference) 
template <typename T> 
inline T const& max (T const& a, T const& b, T const& c) 
{ 
    return max (max(a,b), c); // error, if max(a,b) uses call-by-value 
} 

int main () 
{ 
    ::max(7, 42, 68); // OK 

    const char* s1 = "frederic"; 
    const char* s2 = "anica"; 
    const char* s3 = "lucas"; 
    ::max(s1, s2, s3); // ERROR 

} 

上面代码存在的问题:

问题在于,如果你对三个C字符串调用max()函数,语句

return max (max(a,b), c);会出错。这是因为对于C字符串,max(a,b)会创建一个新的、临时的局部值,可能被引用返回。

问题> 我还是不理解上面的观点。为什么不能使用三个参数的版本来计算三个C字符串的最大值?

"你不能使用三个参数的版本来计算三个C-strings的最大值"的原因是什么?

// 更新

const int* fReturnValue(const int *i)
{
    return i;
}

int main()
{
    int i = 3;
    const int* i4= fReturnValue(&i);

    cout << &i << endl;
    cout << i4 << endl;
}

观察:两行代码返回相同的地址。 因此我认为在函数fReturnValue中,该函数按值返回但不会有害,因为它是指针地址。换句话说,返回的地址仍然有效。 这是真的吗?

据我所知,strcmp不是std命名空间的一部分... - thang
4
@thang:是的,没错。 - K-ballo
1
@thang: cstring吧?http://en.cppreference.com/w/cpp/string/byte/strcmp - K-ballo
cstring是一个包含文件吗?使用哪个C++标准? - thang
4
cstring 是一个头文件,可以在任何 C++ 标准中使用。所有从 C 标准库继承的类型和函数都同时存在于全局命名空间和 std 命名空间中。 - K-ballo
显示剩余3条评论
1个回答

12
问题在于C字符串版本的max返回值,而不是采用传值方式传递参数。泛型的3-way max函数声明为返回引用,但C字符串max返回值,这将导致返回到一个临时变量的引用,在您访问它之前就已经失效。 更新:您添加的新代码与原始问题不等价。以下是等价的代码:
const int*& fReturnValue(const int *i)
{
    return i;
}

注意,fReturnValue返回对局部变量i的引用,其生命周期在函数返回时结束。因此该函数返回对无效对象的引用。尝试使用该更改编译您的代码,几乎每个编译器都会发出警告。


为什么它会返回值?它通过值返回地址。那有什么问题吗?inline char const* max (char const* a, char const* b) 返回传入变量的地址。 - q0987
1
@q0987:它返回值是因为它被声明为这样做。通用的3路“max”将尝试返回对该临时指针持有地址的引用,该临时指针将在“max”返回时死亡(指针而不是其中的地址)。 - K-ballo
1
@q0987:问题在于它返回了一个不再存在的指针的引用。事实上它和int*& f(){ int* ptr = 0; return ptr; }没有任何区别。 - K-ballo
1
@q0987:将代码更改为 const int*& fReturnValue(const int *i){ return i; },你将得到与原始代码相同的行为,即返回对已经失效的临时指针的引用。请注意,我已经多次提到问题出在返回对无效指针地址的引用上。地址没有问题,它仍然有效,但是对持有该地址的临时指针的引用...那就是另一回事了...试试看,你的编译器应该会发出“返回对临时变量的引用”的警告。 - K-ballo
1
非常感谢您的帮助。现在我认为我理解了您的想法。基本上,fReturnValue 中的变量 i 是一个局部变量,返回对局部变量的引用是错误的! - q0987
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接