Suppose I have the following very simple class:
class A
{
public:
static constexpr A make() { return A{}; }
constexpr A() : _v(0) {}
constexpr A& setV(int v) { _v = v; return *this; }
private:
int _v;
};
If I try to use it as follows:
int main()
{
volatile A a1;
a1.setV(10);
//OR
//The optimizer will optimize this chain of constexpr calls into a single "store" instruction
a1 = A::make().setV(10); //More chaining here
return 0;
}
The code will not compile.
I understand why this is true based upon: Defining volatile class object
I know that the solution would be to add an additional method like so:
class A
{
public:
constexpr A() : _v(0) {}
volatile A& setV(int v) volatile { _v = v; return *this; }
constepxr A& setV(int v) { _v = v; return *this; }
private:
int _v;
};
As an aside, I am aware that returning the volatile reference will issue a warning if it is not used.
My question is, is there any way I can avoid code duplication in this case? Both implementations of setV
will always be the same. Thus, if I add/change more methods, I have to maintain them both which could get messy if they aren't so trivial. The only difference is one of type qualifiers...
Since there have been some mentions of this in the comments, I thought I would note that I need volatile here because I am attempting to eventually implement "class overlay" for hardware peripheral register access on an embedded system.
CodePudding user response:
In C 23, you can use an explicit object parameter (also known as deducing this) for this purpose:
class A
{
public:
A() : _v(0) {}
template <class Self>
constexpr auto&& setV(this Self&& self, int v) {
self._v = v; return self;
}
private:
int _v;
};
Unfortunately, as of this writing the only compiler that supports this is the latest version of Microsoft Visual C .
CodePudding user response:
I came up with one possible solution. It's dubious whether it's less verbose than what I had before, but at least I don't have to duplicate the body of the method. In fact, it's similar (in my mind) to the proposed C 23 answer by @ComicSansMS.
class A
{
public:
constexpr A() : _v(0) {}
template <typename T>
static constexpr void setV(T& dest, int src) { dest = src; }
volatile A& setV(int v) volatile { setV(_v, v); return *this; }
constepxr A& setV(int v) { setV(_v, v); return *this; }
private:
int _v;
};
T will be deduced as either int&
or volatile int&
depending on the context. Since its declare constexpr/inline, the compiler/optimizer still boils it down to a few assembly instructions.