Dr. Xperience Dr. Xperience - 9 days ago 6
C++ Question

C++ - Curious case where Template is_Pointer seems to fails

I am trying to make the project for BinaryTree Operations, the following are the class prototype with the code in which I am having the problem.

BinaryTree Class in BinaryTree.h file

template <class T>
class BinaryTree
{
public:
BinaryTree();
BinaryTree(T);
};


The constructor implementation in BinaryTree.cpp

template<class T>
struct isPointer
{
static const bool value=false;
};

template<class T>
struct isPointer<T*>
{
static const bool value=true;
};

template<class T>
BinaryTree<T>::BinaryTree():root(nullptr)
{
//ctor
}

template<class T>
BinaryTree<T>::BinaryTree(T data)
{
if(isPointer<T>::value == true)
{
if(data != nullptr)
{
//Do something
}
}
}


BinaryTreeOperations Class inherit BinaryTree Class and its prototype is defined in BinaryTreeOperations.h

template<class T>
class BinaryTreeOperations:public BinaryTree<T>
{
public:

//Constructors
BinaryTreeOperations();
BinaryTreeOperations(T);
};


While the constructors are defined in BinaryTreeOperations.cpp class

template<class T>
BinaryTreeOperations<T>::BinaryTreeOperations(T data):BinaryTree<T>(data)
{
//ctor
}


The main function in Main.cpp file

int main()
{
cout << "Hello world!" << endl;

BinaryTreeOperations<std::string> b("1");
}


And now the error which g++ is throwing is


no match for 'operator!=' (operand types are
'std::__cxx11::basic_string' and 'std::nullptr_t')


at line

if(data != nullptr)


in BinaryTree Constructor defined in BinaryTree.cpp class

Here comes the issue. I have already defined isPointer structure to check whether the given template is pointer or not. But it seems, in spite of T being std::string g++ is going in
if(isPointer<T>::value == true)

condition.

I don't understand what am I doing wrong? Any sort of guidance will be deeply appreciated.

Answer

This is a case for which constexpr if (C++17) would have been a nice addition: your branches are going to be evaluated at compile-time for every T you pass.

A possible workaround is to leverage enable_if and define two constructor versions depending on the result of your isPointer predicate and let SFINAE do its job

template <class T>
class BinaryTree
{
public:
  BinaryTree();

  template<typename TT = T>
  BinaryTree(TT, typename std::enable_if<!isPointer<TT>::value>::type * = nullptr) {

  }

  template<typename TT = T>
  BinaryTree(TT data, typename std::enable_if<isPointer<TT>::value>::type * = nullptr) {
    if (data != nullptr)
    {
      //Do something (this is a pointer)
    }
  }

};

Example

or alternatively refactor your code keeping in mind the fact that a template is exactly a template and when instantiated it's going to generate code for its arguments on any code branch it defines.