Reputation: 957
I'm trying to specialize a member function on a templated class by a type trait of it's template parameter, but my forward declaration is apparently incorrect. Is there an easy fix?
#include <type_traits>
template <typename T>
class TTest{
public:
T data;
// edited to comment this out, template<typename U>
bool operator !=(const TTest& other) const;
};
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type
TTest<T>::operator !=(const TTest<T>& other) const{
return true;
}
template<typename T>
bool TTest<T>::operator !=(const TTest<T>& other) const{
return false;
}
int main(){
TTest<size_t> t1;
TTest<int> t2;
}
Clang tells me:
templateTest.cpp:13:11: error: out-of-line definition of 'TTest::operator!='
differs from the declaration in the return type
TTest<T>::operator !=(const TTest<T>& other) const{
^
templateTest.cpp:8:8: note: previous declaration is here
bool operator !=(const TTest& other) const;
^
1 error generated.
Upvotes: 0
Views: 264
Reputation: 275936
Tag dispatching is the clean way to do this:
template <typename T>
class TTest{
bool not_equal( const ITest& other, std::true_type /* is unsigned */ ) const;
bool not_equal( const ITest& other, std::false_type /* is unsigned */ ) const;
public:
T data;
bool operator !=(const TTest& other) const {
return not_equal( other, std::is_unsigned<T>() );
}
};
now simply implement the two TTest<T>::not_equal
overloads. Only the one that is actually called for a given T
will be compiled past basic parsing.
Upvotes: 1
Reputation: 109289
It seems like the whole enable_if
shebang is part of the function signature (or I don't really understand the errors). I can get the code to compile and behave as you want if I change it to
template <typename T>
class TTest{
public:
T data;
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
template<typename U>
typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
};
template <typename T>
template <typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
return true;
}
template <typename T>
template <typename U>
typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
return false;
}
Live demo. Of course, this gets a lot less repetitive if you define those operators inline.
A better approach might be to dispatch to different private implementations of the operator logic based on the traits of T
. This removes all the SFINAE verbosity from your code.
template <typename T>
class TTest{
public:
T data;
bool operator!=(const TTest& other) const
{
return not_equal_to(other, typename std::is_unsigned<T>::type());
}
private:
bool not_equal_to(TTest const&, std::true_type) const
{
return true;
}
bool not_equal_to(TTest const&, std::false_type) const
{
return false;
}
};
Upvotes: 4
Reputation: 154035
You declared a member function template of you class template and you are trying to specialize it as a member function. That's not going to fly. Also, the return types differ although end up evaluating to the same thing.
I don't know what you are trying to attempt as the type U
of the member function template isn't even deduced (did you mean the argument to be of type TTest<U>
?). If you want to specialize your member based on a trait, I think you'll either need t overload the operator or use a different approach (e.g., delegating the implementation to a specialized class template):
#include <iostream>
#include <type_traits>
template <typename T>
class TTest{
public:
T data;
template<typename U>
typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
};
template <typename T>
template<typename U>
typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
return true;
}
template <typename T>
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
return false;
}
int main(){
TTest<unsigned int> t1;
TTest<int> t2;
std::cout << std::boolalpha
<< "t1 != t1: " << (t1 != t1) << '\n'
<< "t1 != t2: " << (t1 != t2) << '\n'
<< "t2 != t1: " << (t2 != t1) << '\n'
<< "t2 != t2: " << (t2 != t2) << '\n';
}
Upvotes: 1