I believe I have an ideal solution. It only requires that the test function be altered, so it leaves SmartPtr and Variant alone, which is ideal. It adds a non-defined templated overload to test that has specializations for bool and nullptr that are defined. This directly dispatches bool and nullptr to the desired specialization, but causes link errors on other unhandled types. I'm so glad to have this worked out because I've certainly run into this in many forms myself. I wish you could use explicit of function parameters!!
I got the idea from here: C++ templates that accept only certain types
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant( bool b) : _b(b) { }
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr(std::nullptr_t null) { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> here /*p*/) {
cout << "smartptr version!" << endl;
}
void test(Variant /*v*/) { cout << "variant version!" << endl; }
template<typename T> void test(T t);
template<>
void test<bool>(bool b) {
cout << "bool specialization" << endl;
test(Variant(b));
}
template<>
void test<std::nullptr_t>(std::nullptr_t null) {
cout << "nullptr specialization" << endl;
test(SmartPtr<Object>(nullptr));
}
};
int main(int argc, const char *argv[])
{
Obj o;
Obj c;
Object object;
//o.test(3); // Gives link error LNK2019
o.test(Variant(true)); // calls Variant version
o.test(SmartPtr<Object>(&object)); // calls SmartPtr version
o.test(nullptr); // dispatched to SmartPtr version by nullptr specialization
o.test(true); // dispatched to Variant version by bool specialization
o.test(false); // dispatched to Variant version by bool specialization
return 0;
}
I had already answered with something not ideal, so I leave that answer in tact as what follows:
=============================================
I don't have an ideal solution here, and I don't know the constraints you have on your code so this may not be of functional use to you, but the following is sensible. It disallows code to use nullptr at compile time and relies on a global null_smart constant to be used in all cases where the caller is simply showing no interest in passing an object.
#include <iostream>
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
Variant(std::nullptr_t) {};
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr() { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
const SmartPtr<Object> null_smart;
int main(int argc, const char *argv[])
{
Obj o;
o.test(null_smart); // calls SmartPtr version, without interest in passing object
o.test(true); // calls Variant version
o.test(false); // calls Variant version
return 0;
}
It's cleaner than the true/Variant(false) issue, but still a bit on the picky side.