【问题标题】:Why non-const version is selected over the const version for class?为什么选择非 const 版本而不是类的 const 版本?
【发布时间】:2011-06-29 03:21:44
【问题描述】:

以下是测试代码:

struct A
{
  operator int ();
  operator int () const;
};

void foo (const int);

现在,在调用时:

foo(A());  // calls A::operator int()

为什么是always chooses the non-const version?即使创建operator const int () const; 对调用foo() 也没有任何影响。除了标准参考之外,有人可以从逻辑上解释其背后的原因吗?

【问题讨论】:

    标签: c++ constants overloading overload-resolution


    【解决方案1】:

    A() 给你一个临时的A 不是 const 限定的对象。 A() 表达式是一个右值表达式,是的,但这不会使 A 对象成为 const-qualified。

    由于 A 对象不是 const 限定的,非 const operator int() 是精确匹配,而 const operator int() 需要限定转换,因此选择非 const 重载作为最佳匹配。

    如果您希望它是 const 限定的,您需要明确请求一个 const 限定的 A:

    foo(identity<const A>::type());
    

    identity 定义为

    template <typename T>
    struct identity { typedef T type; };
    

    请注意,operator const int() const 和 operator int() const 之间确实没有区别:结果是右值,只有类类型的右值可以是 const 限定的(int 不是类类型)。

    另请注意,您拥有的void foo(const int) 和void foo(int) 之间没有区别。参数类型上的顶级 const 限定符不影响函数的类型(即,这两个声明的类型都是 void foo(int))。除其他原因外,这是因为调用者是否存在顶级 const 限定符并不重要。无论如何,它都必须制作副本。顶级 const 限定符仅影响函数的定义。

    【讨论】:

    • 即使A 是一个普通的堆栈/堆分配对象(不是临时的),它也会给出相同的结果。
    • 如果对象不是 const 限定的,则非常量重载在重载解析期间是更好的匹配。您是否有理由认为情况并非如此?
    • 我为什么感到困惑的是,即使foo() 收到const int 并且我们有一个A::operator const int () const;,它仍然会选择正常的A::operator int。
    • foo(const int) 和 foo(int) 就调用者而言是相同的。函数参数上的顶级 const 限定符不是函数类型的一部分。 (你可以这样想:调用者并不真正关心是否存在顶级 const 限定符;这无关紧要,因为它只需要知道将要制作一个副本。)跨度>
    • @Tony:好吧,无论如何,每个人都应该有一个identity 模板;它非常有用。 :-) 它的好处是不是演员;如果不需要演员表,则不应使用演员表。即使有人想使用类似“演员”的东西,implicit_cast 也会更可取。 static_cast 和 const_cast 很危险,只能在必要时使用。
    【解决方案2】:

    James McNellis' 的回答确实涵盖了所有内容,但是(我希望)有更多的解释并没有什么坏处。

    所以。

    当你打电话时……

        o.operator int()
    

    ...那么重载选择完全取决于o的常量。

    没有别的了。

    要了解原因,请考虑这个类:

    struct Bar
    {
        void f() {}
        void f() const {}
    };
    

    从技术上讲,这些成员函数不需要是成员函数。它们也可以被选为独立功能。但是他们需要Bar 参数:

    struct Bar
    {};
    
    void f( Bar& ) {}
    void f( Bar const& ) {}
    

    希望现在你更容易看到这一点

    Bar o;
    f( o );
    

    那么第一个函数可以被选中。就是这样。因为如果选择了第二个功能,那么您将永远无法获得第一个功能。因为如果您将对象设为const,那么选择第一个对象会破坏const 的正确性。所以当对象是const时,只能选择第二个,因此,当它不是const时,会选择第一个。

    简而言之,此规则的唯一实际替代方法是始终选择第二个,这会使第一个变得毫无用处,是吗?

    干杯,

    【讨论】:

    • 如果非 const 版本设为私有,它仍会尝试使用该版本而不是公共 const 版本,从而导致“尝试访问私有成员”错误!太烦人了。
    【解决方案3】:

    关于 C++,您必须记住的一条规则:它从不考虑选择重载时返回的值。在这种情况下,由于operator int 函数没有参数,它也不能使用参数列表来缩小选择范围。它只能使用它被调用的对象的常量。由于这是一个新的临时对象,它不是const,所以不会选择const重载。

    【讨论】:

    • Never 可能太强了;在获取函数地址时解析重载时以及解析转换运算符以进行自动转换时,会考虑返回类型。 (当您希望函数在返回类型上重载时,后者可能非常有用。让它返回具有不同转换运算符的代理。)
    猜你喜欢
    • 2011-11-09
    • 1970-01-01
    • 2014-06-09
    • 2020-12-23
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多