Reputation: 7123
I've been trying to adapt this
solution for enabling the existence of ordinary (non-member) functions.
In my case, I have a lot of global string-utility-type functions that take any string type T such that T has has, say, a char const* c_str() const
member function.
The goal is to eliminate weird compiler error messages if the user attempt to pass some type T that has no c_str()
member function, i.e., rather than the compiler saying, "c_str(): no such member function", I'd rather the compiler say, "foo(T&): no such function" where foo
is the global function.
Here is the adapted code:
template<bool B,typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false,T> {
};
//
// This macro is adapted from the linked-to question -- this part works fine.
//
#define DECL_HAS_MEM_FN(FN_NAME) \
template<class ClassType,typename MemFnSig> \
struct has_##FN_NAME##_type { \
typedef char yes[1]; \
typedef char no[2]; \
template<typename T,T> struct type_check; \
template<class T> static yes& has( type_check<MemFnSig,&T::FN_NAME>* ); \
template<class T> static no& has( ... ); \
enum { value = sizeof( has<ClassType>(0) ) == sizeof( yes ) }; \
}
// Declare an instance of the above type that checks for "c_str()".
DECL_HAS_MEM_FN(c_str);
// Define another macro just to make life easier.
#define has_c_str(STRINGTYPE) \
has_c_str_type<STRINGTYPE,char const* (STRINGTYPE::*)() const>::value
//
// A "ValidatedStringType" is a StringType that uses the above machinery to ensure that
// StringType has a c_str() member function
//
template<class StringType>
struct ValidatedStringType {
typedef typename enable_if<has_c_str(StringType),StringType>::type type;
};
// Here's the global function where I want to accept only validated StringTypes.
template<class StringType>
void f( typename ValidatedStringType<StringType>::type const& s ) {
}
struct S { // Class for testing that has c_str().
char const* c_str() const {
return 0;
}
};
struct N { // Class for testing that does not have c_str().
};
using namespace std;
int main() {
S s;
N n;
cout << has_c_str(S) << endl; // correctly prints '1'
cout << has_c_str(N) << endl; // correctly prints '0'
f( s ); // error: no matching function for call to 'f(S&)'
}
However, as shown above, the compiler doesn't "see" the f(S&)
-- why not?
Upvotes: 1
Views: 552
Reputation: 11669
If I understand the question correctly, applying enable_if
to f
itself like
the following will solve the problem:
template<class StringType>
typename enable_if<has_c_str(StringType),StringType>::type
f( StringType const& s ) {....}
Hope this helps.
Upvotes: 1
Reputation: 7105
I can't answer your question ("why not?") but I can provide a work-around. But man, it's ugly. You end up defining a structure and a function for every function:
template<class StringType, class T = typename ValidatedStringType<StringType>::type>
struct f_helper {
void operator()( T const& s ) {
// Put the contents of f() here
}
};
template<class StringType>
void f(StringType const &s) {
f_helper<StringType>()( s );
}
I'm sure there's some preprocessing magic you could write to eliminate some of the boilerplate, but it would also be quite ugly:
#define DECL_VALIDATED_FUNC( RetType, name, Validator, contents ) \
template<class StringType, class T = typename Validator<StringType>::type> \
struct name ## _helper { \
RetType operator()( T const& s ) contents \
}; \
\
template<class StringType> \
RetType name(StringType const& s) { \
name ## _helper<StringType>()( s ); \
}
DECL_VALIDATED_FUNC( void, f, ValidatedStringType, {
// put contents of f() here
}) // note the closing parenthesis
Unfortunately, you can't specify default template arguments to free functions, or else you could have done:
template<class StringType, class T = typename ValidatedStringType<StringType>::type>
void f( T const& s ) {}
Upvotes: 0