2016-12-01 49 views
0

我有一個模板類foo(本質上是一個矩陣)。 foo的模板參數只能是int,floatdouble,即不是const int。原因是我有這些專門的操作符,並且複製常量情況下的操作符似乎是多餘的。我有兩個get_data函數,它們返回一個適當的常量的指針。但我也不想row函數選擇一行並返回const foo,這樣調用方就不能修改返回的對象。正確指針/對象/模板參數

我的問題:

1)我如何讓B常量?

2)我是否應該爲例如foo?

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; 
} 

爲簡單起見,操作員功能已被省略。

+0

我得到這個錯誤,然後(如果我使用'常量int'作爲模板參數,並嘗試使用運算符):不匹配'operator + ='(操作數類型是'foo '和'foo ') – Jonas

+0

sry鏈接錯了,我會尋找更好的 – user463035818

+0

我認爲選項#3,'const_foo',是最乾淨的選項。 –

回答

1

這裏有兩種常量。 Const數據和常量句柄。

我們想要做的是建立健全了四種組合:

  • 常量手柄,常量數據=常數
  • const的手柄,可變數據=常數
  • 可變手柄,常量數據=常量
  • 可變手柄,可變數據=可變

此外,標記返回值爲const沒有meani NG。返回值是一個r值。它將被複制或移動。這不會導致呼叫站點的常量句柄。

所以我們需要在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; 
}