Observe the following program in which a function accepts both the expected type and any type that is a typedef of that type.
//a user defined type
class Widget{};
//a function that takes a Widget
void function (Widget w){}
int main(){
//make a typedef (this is C++11 syntax for a typedef. It's the same thing)
using Gadget = Widget;
//make the two "different types" (well.. they're not really different as you will see)
Widget w;
Gadget g;
//call a function that should ONLY accept Widgets
function(w); //works (good)
function(g); //<- works (I do not want this to compile though)
}
As you can see, a typedef does not actually distinguish a new type. I thought instead to inherit from the type:
//inherit instead
class Gadget: public Widget{};
//make the two "different types"
Widget w;
Gadget g;
//call the function that should ONLY accept widgets
function(w); //works (good)
function(g); //<- works (I do not want this to compile though)
Same problem.
Looking at boost, I thought to try a strong typedef:
#include <boost/serialization/strong_typedef.hpp>
//a user defined type
class Widget{};
//a function that takes the user defined type
void function (Widget w){}
int main(){
//try to strongly typedef
BOOST_STRONG_TYPEDEF(Widget, Gadget)
//make the two "different types"
Widget w;
Gadget g;
//call the function that should ONLY accept widgets
function(w);
function(g);
}
compile errors:
In member function ‘bool main()::Gadget::operator==(const main()::Gadget&) const’:
error: no match for ‘operator==’ (operand types are ‘const Widget’ and ‘const Widget’)
BOOST_STRONG_TYPEDEF(Widget, Gadget)
^
In member function ‘bool main()::Gadget::operator<(const main()::Gadget&) const’:
error: no match for ‘operator<’ (operand types are ‘const Widget’ and ‘const Widget’)
BOOST_STRONG_TYPEDEF(Widget, Gadget)
^
Apparently BOOST_STRONG_TYPEDEF only works on primitive types.
I tried to do inheritance again, but stop the implicit conversion:
//I want the functionality, but these are NOT the same type!
class Gadget: public Widget{
operator Widget() = delete;
};
That did not work either.
Questions:
Basically you need two unrelated classes with the same behavior. I would use a parametrized template for that:
template<int tag> class WidgetGadget { ... };
typedef WidgetGadget<0> Widget;
typedef WidgetGadget<1> Gadget;
BOOST_STRONG_TYPEDEF actually assumes that types are equatable (==
), assignable (=
) and less-than-comparable (<
).
If your type isn't, then the macro results in code that doesn't compile, as you have witnessed. You can roll your own macro or provide implementations for the required operations.
You can find a CUSTOM_STRONG_TYPEDEF
in this answer from februari 2012: How to use comparison operators on variant with contained types?, which explicitly avoids getting the default comparison behaviour
Update Made the example more explicit for your use case, see it Live On Coliru
//a user defined type
class Widget{};
class Frobnicator{};
/////////////////////////////////////////////////////
// copied and reduced from boost/strong_typedef.hpp
#define CUSTOM_STRONG_TYPEDEF(T, D) \
struct D \
/*: boost::totally_ordered1< D */ \
/*, boost::totally_ordered2< D, T */ \
/*> > */ \
{ \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(){}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
D & operator=(const T & rhs) { t = rhs; return *this;} \
explicit operator const T & () const {return t; } \
explicit operator T & () { return t; } \
/*bool operator==(const D & rhs) const { return t == rhs.t; } */\
/*bool operator<(const D & rhs) const { return t < rhs.t; } */\
};
CUSTOM_STRONG_TYPEDEF(Widget, Gadget)
CUSTOM_STRONG_TYPEDEF(Frobnicator, Normalcy)
void acceptWidget(Widget){}
void acceptGadget(Gadget){}
void acceptFrobnicator(Frobnicator){}
void acceptNormalcy(Normalcy){}
int main(){
//make the two "different types" (well.. they're not really different as you will see)
Widget w;
Gadget g;
//call a function that should ONLY accept Widgets
acceptWidget(w); //works (good)
acceptGadget(g);
//acceptWidget(g); // Error
//acceptGadget(w); // Error
// but we can enjoy conversions if we summon them
acceptWidget(static_cast<Widget&>(g));
Frobnicator f;
Normalcy n;
acceptFrobnicator(f);
acceptNormalcy(n);
}
Maybe you could use private inheritance and some using
s?
class Gadget : Widget { using Widget::Widget; using Widget::foo; ... };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With