Oxdeadbeef
Oxdeadbeef

Reputation: 1063

Class template with both pointer type and regular type

I define a Node class with template for its value type

template<class T>
class Node {
  T val;
  public:
    Node (T & v) : val (v) {}
    ...
    void print() { cout << v << endl; }
}

Most times, the node value of interest will be an object class, say class Foo. In that case, use Node<Foo *> will be more convenient. But it could also be that the node will hold primitive time, say int. Then use Node<int> will suffice.

The problem is, some of the function may need to behave differently based on whether T is a pointer type or not. For example, print should cout << *v when it is and cout << v otherwise.

What I've tried is to define both:

template<class T>
class Node {
  T val;
  public:
    Node (T & v) : val (v) {}
    ...
    void print() { cout << v << endl; }
}

template<class T>
class Node<T*> {
  T* val;
  public:
    Node (T* v) : val (v) {}
    ...
    void print() { cout << *v << endl; }
}

It now can choose the appropriate definition based on whether it's Node<int> or Node<int *> But the problem become, the two definitions will share many code. I'm wondering whether there's better way to achieve this.

Upvotes: 8

Views: 44552

Answers (3)

user2366975
user2366975

Reputation: 4700

Had a similar issue, this is how I could solve it:

#include <iostream>
#include <memory>

template<typename T> struct is_shared_ptr : std::false_type {};
template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

template<typename T>
void is_shared(const T& t) {
    std::cout << is_shared_ptr<T>::value << std::endl;
}

template<typename T>
T* to_ptr(T& obj) {
    std::cout << "Ref to Ptr" << std::endl;
    return &obj;
}

template<typename T>
T* to_ptr(std::shared_ptr<T> & obj) {
    std::cout << "SharedPtr to Ptr" << std::endl;
    return obj.get();
}

template<typename T>
T* to_ptr(T* obj) {
    std::cout << "Ptr to Ptr" << std::endl;
    return obj;
}

// This is my usecase: Depending on the type, `.` or `->` is needed.
// But the if T = shared_ptr<Text>, `->` did not work...
template<typename T>
void free_printsize(T t){
    auto t_ptr = to_ptr(t);
    t_ptr->printsize();
}

class Text{
    std::string text_;
public:
    Text(const std::string &t){ text_ = t; }
    void printsize() const{
        std::cout << "Size of '" << text_ << "': " << text_.size();
    }
};

int main()
{    
    std::cout << "Testing value object: ";
    Text test("Hello");    
    is_shared(test);
    free_printsize(test);

    std::cout << "\n\nTesting ptr: ";
    Text* test_ptr = &test;
    is_shared(test_ptr);
    free_printsize(test_ptr);

    std::cout << "\n\nTesting shared ptr: ";
    std::shared_ptr<Text> test_sp = std::make_shared<Text>("Hello");
    is_shared(test_sp);
    free_printsize(test_sp);

    return 0;
}

Output:

Testing value object: 0
Ref to Ptr
Size of 'Hello': 5
Testing ptr: 0
Ptr to Ptr
Size of 'Hello': 5
Testing shared ptr: 1
SharedPtr to Ptr
Size of 'Hello': 5
Process exited with code: 0

Upvotes: 0

Alok
Alok

Reputation: 127

Well, there is one more way of doing this. You should use type traits, they are evaluated at compile time. This is how you can modify.

template<class T>
class Node {
  T val;
  public:
    Node (T & v) : val (v) {}
    ...
    void print() { 
      if(std::is_pointer<T>::value)
        cout << *v << endl;
      else
        cout << v << endl;
    }
}

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275310

See this: C++ template specialization, calling methods on types that could be pointers or references unambiguously

The same technique should work here, allowing you to deal with the val as a reference (or a pointer) uniformly in both cases.

CRTP may help reduce code duplication, allowing for common code for two specializations without any overhead, as well.

Note that ownership semantics get tricky when you sometimes use a pointer and sometimes an instance -- what is the lifetime of val if sometimes it is a pointer of an argument, and other times it is a copy of the argument, and how to you enforce it?

Upvotes: 5

Related Questions