【问题标题】:Correct constness with pointer / object / template parameter使用指针/对象/模板参数纠正 constness
【发布时间】:2016-12-01 13:12:59
【问题描述】:

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

我的问题:

1) 我怎样才能使 B 成为常量?

2) 我应该为例如创建运算符函数吗?富?

2) 我应该创建一个reference_foo 类吗?

template <class T>
class foo
{
    using uint8_t = unsigned char;

    int rows = 0;
    int cols = 0;    
    T * 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 T[rows * cols];
    }

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

    T * get_data()
    {
        return data;
    }

    T const * get_data() const
    {
        return data;
    }

    const foo row(int r) const
    {
        foo t;
        t.rows = 1;
        t.cols = cols;
        t.reference = true;
//        t.data = get_data() + r * cols; // ERROR: invalid conversion from 'const uint8_t*' to 'uint8_t*'
        t.data = const_cast<T*>(get_data()) + r * cols; // Not pretty, but "ok" if the returned object is const
        return t;
    }
};

int main() 
{
    const foo<int> A(2, 1);
//    A.get_data()[0] = 1; // ERROR: assignment of read-only location, perfectly catched by compiler
    auto B = A.row(1);
    B.get_data()[0] = 1; // B is not const... overwritten...

    return 0;
}

为简单起见,省略了运算符函数。

【问题讨论】:

  • 我得到这个然后错误(如果我使用const int作为模板参数并尝试使用运算符):'operator+='不匹配(操作数类型是'foo'和' foo')
  • sry 链接错误,我会寻找更好的
  • 我认为选项 #3 const_foo 是最干净的选项。
  • @SamVarshavchik 我喜欢它,从语法的角度来看(它类似于const_iterator),但是我必须制作大量的函数来处理这两个类在一起。
  • 不一定,如果你让foo继承自const_foo,并实现const_foo中的所有const方法,使用protected指针,以及存储在@987654337中的标志@ 和 using 来自 foo 的所有 const 方法。对于大多数 C++ 库容器,iterator 继承自 const_iterator

标签: c++ templates constants


【解决方案1】:

这里有 2 种 constness。常量数据和常量句柄。

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

  • 常量句柄,常量数据 = 常量
  • const 句柄,可变数据 = const
  • 可变句柄,const data = const
  • 可变句柄,可变数据 = 可变

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

所以我们需要在两个地方检测get_data() 的常量。 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    相关资源
    最近更新 更多