Convert bool to nullptr_t automatically

I have the following code with a custom Variant class and a SmartPtr custom class:

using namespace std; class Object { public: }; template<typename T> class SmartPtr { public: template<typename Y> explicit SmartPtr(Y* p) { p_ = p; } SmartPtr(std::nullptr_t) { p_ = nullptr; } private: T* p_; }; class Variant { public: Variant(bool b) : _b(b) { } private: bool _b; }; class Obj { public: void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; } void test(Variant /*v*/) { cout << "variant version!" << endl; } }; int main(int argc, const char *argv[]) { Obj o; o.test(nullptr); // calls SmartPtr version o.test(true); // calls Variant version o.test(false); // -> compiler error: ambiguous call to overloaded function return 0; } 

I assume that the boolean false can be converted to both Variant and 0, then to nullptr, and then to SmartPtr, which causes this error.

Are there any chances to avoid this conversion?

For an API library user who works with 'o.test (true); but requires something like "o.test (Variant (false)); compiling is not very intuitive.

+5
source share
1 answer

I believe that I have the perfect solution. It only requires the test function to be changed, so it only leaves SmartPtr and Variant, which is ideal. It adds an undefined template overload for validation, which has specializations for bool and nullptr, which are defined. This directly sends bool and nullptr to the required specialization, but causes errors in references to other raw types. I am so glad that it worked, because of course I come across this in many forms. I want you to be able to use explicit function parameters!

I got an 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 already answered with something not perfect, so I leave this answer tactfully, as follows:

===============================================

I don't have the perfect solution here, and I don't know the limitations that you have on your code, so this may not be functional for you, but the following is reasonable. It prohibits the use of nullptr code at compile time and relies on the global constant null_smart, which will be used in all cases where the calling object simply does not show interest in passing the 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 is cleaner than the true / Variant (false) issue, but still a bit on the picky side.

+1
source

Source: https://habr.com/ru/post/1201330/


All Articles