Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specialization for any vector

I'd like to define a function in a template class for different cases of T. My problem is to define the case where T is a vector (whatever it contains).

Here's one of my first attempts :

template<typename T>
class Test{
protected:
    T *val;
public:
    Test(T* v):v(val){};
    static string typeName();
};

template<typename T>
string Test<T>::typeName(){
    return "other";
}

template<>
string Test<int>::typeName(){
    return "int";
}

template<typename T>
string Test<vector<T>>::typeName(){
//error: invalid use of incomplete type ‘class Test<vector<T>>’
    return "vector<" + Test<T>::typeName() + ">";
}

I could resolve this error by defining :

template<typename T>
class Test<vector<T>>{
    // ... (the same thing)
}

but I don't want to do like this, because my class is big, and I don't want to write again the same functions. I would also like to avoid making my class inherit from another, if possible.

By searching here, I read about SFINAE but I'm a beginner with it. I tried every way I could but nothing worked. Here's another attempt :

#include <type_traits>

template<typename T,typename E=void>
class Test{
protected:
    T *val;
public:
    Test(T* v);
    static string typeName();
    static void bidon();
};

template<typename T,typename E>
string Test<T,E>::typeName(){
    return "other";
}

template<>
string Test<int>::typeName(){
    return "int";
}

template<typename T> struct IsVector            : public std::false_type{};
template<typename T> struct IsVector<vector<T>> : public std::true_type {};

template<typename T,typename E>
    string Test<T,typename std::enable_if<IsVector<T>::value,E>::type>::typeName(){
    return "vector<" + Test<typename T::value_type>::typeName() + ">";
}

I had the same error. What's wrong with it ? Am I trying to do impossible things?

like image 943
Gilles Avatar asked Apr 12 '26 00:04

Gilles


2 Answers

I think you can achieve what you want with this. The result is the same even though the approach is a little bit different.

template <typename V>
struct typeName {};

// Handle pointers
template <typename V>
struct typeName<V*> {
    static string name;
};
template <typename V>
string typeName<V*>::name = typeName<V>::name + "*";

template <>
struct typeName<string> {
    static string name;
};
string typeName<string>::name = "string";


template <>
struct typeName<int> {
    static string name;
};
string typeName<int>::name = "int";

template <typename V>
struct typeName<vector<V>> {
    static string name;
};

template <typename V>
string typeName<vector<V>>::name = "vector<" + typeName<V>::name + ">";


template <typename V>
struct typeName<set<V>> {
    static string name;
};

template <typename V>
string typeName<set<V>>::name = "set<" + typeName<V>::name + ">";


int main() {
    cout << typeName<vector<string>>::name << endl;   
    cout << typeName<set<string>>::name << endl;   
    cout << typeName<vector<set<string*>*>>::name;   
}

stdout

vector<string>
set<string>
vector<set<string*>*>
like image 152
stardust Avatar answered Apr 13 '26 14:04

stardust


Since your typeName method is both static and public, you can use a free helper function like this:

template< typename T > struct is_vector : std::false_type {};

template< typename... Ts > struct is_vector<vector<Ts...>> : std::true_type {};

// Standard-confirming compilers will accept the above specialization, but
// if your compiler doesn't like the above, you need to manually provide all
// template arguments to detect std::vector properly:

// template< typename T, typename Allocator >
// struct is_vector<vector<T,Allocator>> : std::true_type {};

template<typename T>
typename std::enable_if< !is_vector<T>::value, string >::type testTypeName(){
    return "other";
}

template<typename T>
typename std::enable_if< is_vector<T>::value, string >::type testTypeName(){
    return "vector<" + Test<typename T::value_type>::typeName() + ">";
}

template<>
string Test<int>::typeName(){
    return "int";
}

template<typename T>
string Test<T>::typeName(){
    return testTypeName<T>();
}
like image 28
Daniel Frey Avatar answered Apr 13 '26 13:04

Daniel Frey