有没有办法让一个公共成员,从类外部无法修改,没有访问器包装函数?

时间:2017-02-18 17:47:33

标签: c++ c++17

据我所知,这似乎是不可能直接的。使const成员使每个人都成为const。我想有一个只读属性,但我想避免典型的“getter”。我喜欢const public,mutable private。这在C ++中是否可行? 目前我能想到的只是模板和friend的一些技巧。我现在正在调查这个。

可能看起来像是一个愚蠢的问题,但我之前对这里的答案感到惊讶。

4 个答案:

答案 0 :(得分:3)

一个可能的解决方案可以基于一个内部类,其中外部类是朋友,如下所示:

struct S {
    template<typename T>
    class Prop {
        friend struct S;
        T t;
        void operator=(T val) { t = val; }
    public:
        operator const T &() const { return t; }
    };

    void f() {
        prop = 42;
    }

    Prop<int> prop;
};

int main() {
    S s;
    int i = s.prop;
    //s.prop = 0;
    s.f();
    return i, 0;
}

如示例所示,类S可以在其成员函数中修改属性(请参阅S::f)。另一方面,属性不能以任何其他方式修改,但仍然通过给定的运算符读取,该运算符返回对实际变量的const引用。

答案 1 :(得分:3)

似乎还有另一个更明显的解决方案:使用一个公共const引用成员,指向私有的,可变的成员。 live code here

#include <iostream>

struct S {
  private:
    int member;
  public:
    const int& prop;
    S() : member{42}, prop{member} {}
    S(const S& s) : member{s.member}, prop{member} {}
    S(S&& s) : member(s.member), prop{member} {}
    S& operator=(const S& s) { member = s.member; return *this; }
    S& operator=(S&& s) { member = s.member; return *this; }
    void f() { member = 32; }
};

int main() {
    using namespace std;

    S s;
    int i = s.prop;

    cout << i << endl;
    cout << s.prop << endl;

    S s2{s};

    // s.prop = 32; // ERROR: does not compile
    s.f();

    cout << s.prop << endl;
    cout << s2.prop << endl;

    s2.f();
    S s3 = move(s2);

    cout << s3.prop << endl;

    S s4;
    cout << s4.prop << endl;
    s4 = s3;
    cout << s4.prop << endl;
    s4 = S{};
    cout << s4.prop << endl;
}

答案 2 :(得分:0)

我喜欢@skypjack的答案,但是会以某种方式写出来:

#include <iostream>

template <class Parent, class Value> class ROMember {
  friend Parent;
  Value v_;
  inline ROMember(Value const &v) : v_{v} {}
  inline ROMember(Value &&v) : v_{std::move(v)} {}
  inline Value &operator=(Value const &v) {
    v_ = v;
    return v_;
  }
  inline Value &operator=(Value &&v) {
    v_ = std::move(v);
    return v_;
  }
  inline operator Value& () & {
    return v_;
  }
  inline operator Value const & () const & {
    return v_;
  }
  inline operator Value&& () && {
    return std::move(v_);
  }

public:
  inline Value const &operator()() const { return v_; }
};

class S {
  template <class T> using member_t = ROMember<S, T>;

public:
  member_t<int> val = 0;
  void f() { val = 1; }
};

int main() {
  S s;
  std::cout << s.val() << "\n";
  s.f();
  std::cout << s.val() << "\n";
  return 0;
}

一些enable_if缺少真正的核心通用,但精神是让它可以重复使用并让呼叫看起来像吸气剂。

这确实是一个带有friend技巧

答案 3 :(得分:-1)

您可以使用奇怪的重复模板模式,并在属性类中与超类成为朋友,如下所示:

#include <utility>
#include <cassert>

template<typename Super, typename T>
class property {
  friend Super;

  protected:
    T& operator=(const T& val)
    { value = val; return value; }

    T& operator=(T&& val)      
    { value = val; return value; }

    operator T && () &&
    { return std::move(value); }

  public:
    operator T const& () const& 
    { return value; }

  private:
    T value;
};

struct wrap {
  wrap() {
    // Assign OK
    prop1 = 5; // This is legal since we are friends
    prop2 = 10;
    prop3 = 15;

    // Move OK
    prop2 = std::move(prop1);
    assert(prop1 == 5 && prop2 == 5);

    // Swap OK
    std::swap(prop2, prop3);
    assert(prop2 == 15 && prop3 == 5);
  }
  property<wrap, int> prop1;
  property<wrap, int> prop2;
  property<wrap, int> prop3;
};

int foo() {
  wrap w{};
  w.prop1 = 5;    // This is illegal since operator= is protected
  return w.prop1; // But this is perfectly legal 
}