如何使编译器更喜欢C ++中的const方法重载?

Wil*_*mKF 5 c++ const lazy-initialization perfect-forwarding c++17

我有一个带有a const和非const重载的类的C ++成员函数。

Class Example {
public:
  int const & Access() const;
  int       & Access();

  [...]
};
Run Code Online (Sandbox Code Playgroud)

我希望首选const版本,因为在我的代码中性能要好得多,非const版本会导致创建基础共享对象的副本以允许修改。

现在,如果我的调用者有一个非常量Example对象,则即使未修改结果int,也会使用非常量Access()方法。

Example example;

if ( modify ) {
    example.Access() = 23;    // Need to perform expensive copy here.
} else {
    cout << example.Access(); // No need to copy, read-only access.
}
Run Code Online (Sandbox Code Playgroud)

有没有一种方法,例如区分返回值的左值和右值使用,也许使用带有模板的完美转发,以在C ++ 17中创建类似的机制,从而允许调用者拥有一种语法,而编译器仅使用非语法。 const版本是否修改了返回值?

我需要这样做的另一个示例是operator -> ()我有一个const和非const版本的运算符。当调用方法时,const我希望编译器更喜欢的const版本operator -> ()

Class Shared {
  public:
    int Read() const;
    void Write(int value);

    [...]
};

template <typename BaseClass>
class Callback {
public:
  BaseClass const * operator -> () const; // No tracking needed, read-only access.
  BaseClass       * operator -> ();       // Track possible modification.

  [...]
};

typedef Callback<Shared> SharedHandle;

Shared shared;
SharedHandle sharedHandle(&shared);

if ( modify ) {
  sharedHandle->write(23);
} else {
  cout << sharedHandle->Read();
}
Run Code Online (Sandbox Code Playgroud)

Art*_*yer 6

最简单的方法是成为一个CAccess成员(就像cbegin在stdlib容器上一样):

class Example {
public:
  int const & Access() const;
  int       & Access();
  int const & CAccess() const { return Access(); }
  // No non-const CAccess, so always calls `int const& Access() const`
};
Run Code Online (Sandbox Code Playgroud)

CAccess如果您不进行修改,则需要记住要打电话给它。

您也可以返回一个代理:

class Example;

class AccessProxy {
  Example& e;
  explicit AccessProxy(Example& e_) noexcept : e(e_) {}
  friend class Example;
public:
  operator int const&() const;
  int& operator=(int) const;
};

class Example {
public:
  int const & Access() const;
  AccessProxy Access() {
      return { *this };
  }
private:
  int & ActuallyAccess();
  friend class AccessProxy;
};

inline AccessProxy::operator int const&() const {
    return e.Access();
}

inline int& AccessProxy::operator=(int v) const {
    int& value = e.ActuallyAccess();
    value = v;
    return value;
};
Run Code Online (Sandbox Code Playgroud)

但是这里的缺点是类型不再是int&,这可能会导致一些问题,并且只会operator=被重载。

operator通过制作一个模板类,第二个可以很容易地应用于s:

#include <utility>

template<class T, class Class, T&(Class::* GetMutable)(), T const&(Class::* GetImmutable)() const>
class AccessProxy {
  Class& e;
  T& getMutable() const {
      return (e.*GetMutable)();
  }
  const T& getImmutable() const {
      return (e.*GetImmutable)();
  }
public:
  explicit AccessProxy(Class& e_) noexcept : e(e_) {}
  operator T const&() const {
      return getImmutable();
  }
  template<class U>
  decltype(auto) operator=(U&& arg) const {
      return (getMutable() = std::forward<U>(arg));
  }
};

class Example {
public:
  int const & Access() const;
  auto Access() {
      return AccessProxy<int, Example, &Example::ActuallyAccess, &Example::Access>{ *this };
  }
private:
  int & ActuallyAccess();
};
Run Code Online (Sandbox Code Playgroud)

(尽管AccessProxy::operator->也需要定义)

并且第一种方法对operator成员不起作用(除非您愿意更改sharedHandle->read()sharedHandle.CGet().read()