为什么编译器在这个const函数重载问题上更聪明?

时间:2010-04-23 23:57:59

标签: c++ function const overloading

以下代码无法编译:

#include <iostream>
class Foo {
  std::string s;
 public:
  const std::string& GetString() const { return s; }
  std::string* GetString() { return &s; }
};

int main(int argc, char** argv){
  Foo foo;
  const std::string& s = foo.GetString(); // error
  return 0;
}

我收到以下错误:

const1.cc:11: error: 
invalid initialization of reference of type 'const std::string&' 
from expression of type 'std::string*

它确实有意义,因为foo不是const Foo类型,而只是Foo,所以编译器想要使用非const函数。但是,为什么我不能通过查看我赋给它的(类型)变量来识别我想调用const GetString函数?我发现这种情况令人惊讶。

4 个答案:

答案 0 :(得分:10)

返回类型是由实际调用的重载函数确定的,它从不构成重载决策本身的一部分。 (如果没有使用返回类型怎么办?)

const不是返回值的问题,因为你可以将非const对象绑定到const引用,这是你的函数返回一个指针的事实你没有取消引用。

由于foo不是const,非const GetString()被调用 - 它更适合非const对象。你需要:

const std::string& s = *foo.GetString();

答案 1 :(得分:3)

我不记得为什么他们不允许在返回类型上重载(我认为这是因为返回值可以被丢弃,因此函数不会是不同的),但你可以使用const_cast提示修复问题到编译器: const std::string& s = const_cast<const Foo&>(foo).GetString();

答案 2 :(得分:1)

扩展MarkB's answer,只是略微说明可能比丢弃返回值更糟糕的情况:

#include <cmath>
#include <complex>

struct Foo {
   int x;
   double y;
   std::complex<char> z;
   // etc, etc
};

int evaluate(Foo f) { return f.x; }
double evaluate(Foo f) { return f.y; }
std::complex<char>(Foo f) { return f.z; }
//etc, etc

template <typename T> class Thingamajig
{
public:
    enum { value = sizeof (T); };
};

template <>
class Thingamajig<double> class Thingamajig
{
public:
    int value(int a) { return a/3; }
};

template <typename T> Thingamajig<T> thingamatize(const T& t)
{
    return Thingajamig<T>();
}

Foo myfoo = { 1, 2, 3 };
size_t result = sizeof(thingamatize(std::abs(4 - evaluate(myfoo))).value('B'));

确定double evaluate(Foo f)应该被选择是一个非常漫长的过程。

另请阅读:http://blogs.msdn.com/ericlippert/archive/2006/03/31/delegates-lambdas-type-inference-and-long-playing-records.aspx

“您今天要为编译器创建多少工作?™”

答案 3 :(得分:0)

选择过载时不使用函数的返回类型。这就是语言的运作方式。

但是,根据上下文选择隐式转换 。所以,技术上你可以通过返回一些可以隐式转换为引用和指针的东西来编译它。

#include <iostream>
#include <string>

struct Evil
{
    std::string* p;
    Evil(std::string* p): p(p) {}
    operator std::string*() const { return p; }
    operator std::string&() const { return *p; }
};

struct ConstEvil
{
    const std::string* p;
    ConstEvil(const std::string* p): p(p) {}
    operator const std::string*() const { return p; }
    operator const std::string&() const { return *p; }
};

class Foo {
    std::string s;
public:
    ConstEvil GetString() const { return ConstEvil(&s); }
    Evil GetString() { return Evil(&s); }
};

int main(int argc, char** argv){
    Foo foo;
    const std::string& s = foo.GetString(); // ok
    return 0;
}

但真正的答案是在constness上重载的函数应该具有类似的返回类型。 (我想这就是惯例“使用可变事物的指针,对不可变事物的const引用”只是分解了。)