0
votes

I am trying to achieve type checking of template class parameters by disallowing implicit type conversions such as string->bool thereby throwing compile error. The specific scenario is a simple one as follows:

#include <iostream>
#include <string>
using namespace std;

template <class T>
class myPair {
T a, b;
public:
  myPair(T first, T second ) {
  a = first;
  b = second;
  }
  void test();
};

typedef myPair<bool> boolParm;

template<class T>
void myPair<T>::test() {
  if(a == true) {
  cout << "a is true" << endl;
  } else {
  cout << "a is false" << endl;   
  }
  if(b == true) {
  cout << "b is true" << endl;
  } else {
  cout << "b is false" << endl;
  }
}

int main() {
  boolParm myObj(false, "false");
  myObj.test();
  return 0;
}

The output of the above scenario is undesirable since the user may inadvertently pass 2 different types: bool and string and receive the first one as false (correct since passed as bool) but the second one will be true (incorrect since implicit type conversion from string to bool). I wish to restrict the user code in main() to throw compile errors and disallowing string/int parameters to pass in the constructor. It should only allow bool. I tried by using an overloaded constructor myPair(bool first, string second) but it didn't match since I guess the implicit type conversion from string->bool happens before the constructor is called. Is there any solution using template specializations in this scenario? Any help is highly appreciated Thanks

2

2 Answers

1
votes

One workaround is to add a templated factory function to create the myPair.

template <typename T>
myPair<T> makeParam(T a, T b) {
    return myPair<T>(a, b);
}

That will fail to compile with ambiguous template parameter T if the types don't match. You can extend this with template specializations explicitly forbidding certain types for T. Your main function will then look something like:

int main() {
    boolParm myObj = makeParam(false, "false");
    myObj.test();
    return 0;
}

Alternatively change the constructor:

template <typename U, typename V>
myPair(U a, V b);

And specialize as necessary

An example of such specialization:

template <class T>
class myPair {
    T a, b;
public:
    template <typename U, typename V> // generic version
    myPair(U first, V second)
    {
        // intentionally fail to compile
        static_assert(false, "don't support generic types");
    }

    template <> // template specialization
    myPair(T first, T second)
    {
        // explicitly require exactly type T
        a = first;
        b = second;
    }
};
0
votes

It is indeed weird behavior at first glance; but as far as I can say, you can't prohibit that - not for primitive types like bool, anyway.

The implicit conversion of parameters happen before you get a say on it, and it seems there is an implicit primitive type conversion from char const * to bool.

See also e.g. this other question: Why does a quoted string match bool method signature before a std::string?