I would separate the two constructors. And take the rest of the current constructor code apart, e.g. to an initialize
function.
- [c++11] Using
enable_if
. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize() {
std::cout << "initialize\n";
//
// Other constructor things...
//
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<not std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
- [c++20] Using a
requires
clause. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize()
{
std::cout << "initialize\n";
//
// Other constructor things...
//
}
MyClass(const Options<S>& opts) requires std::is_same<S,T>::value {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
MyClass(const Options<S>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
Other options if you want to keep the current signature completely unchanged are:
- [c++11] Using partial specializations of a helper class. [Demo]
template <typename U, typename V, bool ConstructorsAreEqual>
struct MyThingInitializer;
template <typename U, typename V>
struct MyThingInitializer<U, V, true> {
static void initialize(const Options<U>& opts, std::shared_ptr<V> thing) {
std::cout << "Same same\n";
thing.reset(new V(opts.get_s_thing()));
}
};
template <typename U, typename V>
struct MyThingInitializer<U, V, false> {
static void initialize(const Options<U>&, std::shared_ptr<V> thing) {
std::cout << "But different\n";
thing.reset(new V());
}
};
MyClass(const Options<S>& opts) {
MyThingInitializer<S, T, std::is_same<S, T>::value>::initialize(opts, thing);
//
// Other constructor things...
//
}
- [c++20] Using
if constexpr
. [Demo]
MyClass(const Options<S>& opts) {
if constexpr(std::is_same<S,T>::value)
{
std::cout << "Same same\n";
thing.reset(new S(opts.get_s_thing()));
}
else
{
std::cout << "But different";
thing.reset(new T());
}
//
// Other constructor things...
//
}
In another order of things, it's not recommended to use new
with smart pointers. You may want to change your lines:
thing.reset(new S(opts.get_s_thing()));
thing.reset(new T());
for:
std::make_shared<U>(opts.get_s_thing()).swap(thing);
std::make_shared<T>().swap(thing);