使用指针/对象/模板参数更正constness

时间:2016-12-01 13:12:59

标签: c++ templates const

我有一个模板类foo(本质上是一个矩阵)。 foo的模板参数只能是intfloatdouble,即不是const int。这样做的原因是我有专门的运算符,复制const情况的运算符似乎是多余的。我有两个get_data函数,它们返回一个具有适当const的指针。但我也不是一个选择单行并返回row的{​​{1}}函数,这样调用者就无法修改返回的对象。

我的问题:

1)我如何制作B const?

2)我应该为例如foo?

2)我应该制作一个reference_foo类吗?

const foo

为简单起见,省略了操作员功能。

1 个答案:

答案 0 :(得分:1)

这里有两种常数。 Const数据和const句柄。

我们想要做的是从四种组合中创造理智:

  • const handle,const data = const
  • const handle,mutable data = const
  • 可变句柄,const data = const
  • 可变句柄,可变数据=可变

此外,将返回值标记为const没有意义。返回值是r值。它将被复制或移动。这不会在调用站点产生const句柄。

因此我们需要在get_data()的2个位置检测常数。 C ++首先为我们做了一个const重载。然后我们必须推迟到另一个模板,该模板在推导的上下文中进行评估,以便我们可以使用std::enable_if

#include <cstddef>
#include <utility>
#include <type_traits>

// default getter - element != const element
template<class Element, typename = void>
struct data_getter
{
  using element_type = Element;
  using const_element_type = std::add_const_t<element_type>;

  // detect mutable container    
  element_type* operator()(element_type ** pp) const
  {
    return *pp;
  }

  // detect const container
  const_element_type* operator()(element_type * const * pp) const
  {
    return *pp;
  }


};

// specific specialisation for element == const element    
template<class Element>
struct data_getter<Element,
std::enable_if_t<
  std::is_same<Element, std::add_const_t<Element>>::value>>
{
  // in this case the container's constness is unimportant, so
  // we use const because it means only writing one method
  Element* operator()(Element *const* p) const
  {
    return *p;
  }
};

template <class T>
class foo
{
  public:
  using element = T;
  using const_element = std::add_const_t<element>;

    int rows = 0;
    int cols = 0;    
    element * data = nullptr;
    bool reference = false;

public:
    foo() = default;
    //foo(const foo&) // this is not included here for simplicity
    //foo& operator=(const foo&) // this is not included here for simplicity


    foo(int r, int c) : rows(r), cols(c)
    {
        data = new element[rows * cols];
    }

    ~foo()
    {
        if (!reference)
        {
            delete[] data;
        }
    }

    decltype(auto) get_data()
    {
      // defer to getter
      return data_getter<element>()(&data);
    }

    decltype(auto) get_data() const
    {
      // defer to getter
      return data_getter<const_element>()(&data);
    }

    // this will return a mutable container of const data    
    foo<const_element> row(int r) const
    {
        foo<const_element> t;
        t.rows = 1;
        t.cols = cols;
        t.reference = true;
        t.data = get_data() + r * cols;
        return t;
    }
};

int main() 
{
    foo<int> A(2, 1);
    A.get_data()[0] = 1;

  auto AC = A.row(0);
  auto x = AC.get_data()[0];   // fine

//  AC.get_data()[0] = 1; // assignment of read-only location

    return 0;
}