I'm trying to achieve, for lack of a better term, a "view" pattern in C++. It's probably most comparable to "views" in the Database world, where one can make a query, and then perhaps aggregate the data, or turn otherwise interact with it without actually duplicating the data.
I've tried to reduce it down to a simple example, where the Data is any integer, and the View is the opposite of that integer (x, -x). This example is pretty contrived, but when we're talking about matrices or other complex forms of data, I could see this pattern become quite useful, especially with respect to generic programming
The code compiles, and fundamentally, works. Unfortunately, there are alternative implementations of this pattern, and that's why I'm asking for a code review. In the long term, are there any reasons that I should be concerned about using this design pattern in my code? Perhaps with respect to:
- Avoiding, Identifying Bugs
- Class Flexibility
- Code Readability
Some alternative implementations I can think of:
- Using raw pointers instead of const references for the Data class
- Using inheritance and implementing the "view" functions within the Data class
There are some important things to note about this code as well. The View class can not be copied or moved, meaning that its lifespan is identical to that of the Data class. There is also the flexibility of being able to mutate the View class when Data::view() is called, for example, setting a scalar. The two classes are mutual friends, but the Data only really has a use for the constructor of the View.
Data.h
#ifndef DATA_H
#define DATA_H
#include "View.h"
class Data {
public:
Data(const int value) :
view_(*this),
value_(value)
{}
const View& view() const { return view_; }
int value() const { return value_; }
private:
friend View;
const int value_;
const View view_;
};
#endif // DATA_H
View.h
#ifndef VIEW_H
#define VIEW_H
class View {
public:
View() = delete;
View(const View&) = delete;
View(View&&) = delete;
View operator=(const View&) = delete;
View operator=(View&&) = delete;
int value() const;
private:
friend class Data;
const Data& data_;
View(const Data& data) : data_(data) {}
};
#endif // VIEW_H
View.cpp
#include "View.h"
#include "Data.h"
int View::value() const {
return -data_.value_;
}
Example.cpp
#include "Data.h"
#include <iostream>
int main() {
Data data{ 5 };
const View& view = data.view();
std::cout << data.value() << std::endl;
std::cout << view.value() << std::endl;
}