Possible Duplicate:
Is it possible to write a C++ template to check for a function's existence?
I have a function f
that receives a value val
of type T
(templated). Is there any way to call a member function on val
only if the type has such a member function?
Example:
struct Bar {
void foo() const {}
};
template<class T>
void f(T const& val) {
// Is there any way to call *only* if foo() is available on type T?
// SFINAE technique?
val.foo();
}
int main() {
Bar bar;
f(bar);
f(3.14);
}
Sounds like the SFINAE technique to me, maybe using boost::enable_if, but I don't know how to make it work here. Note that I can't easily change the Bar
type in the example. I know it would be easy if Bar
contained some certain typedef, etc., which signals that the function is available.
Needless to say, I don't know the set of types T
that f
will be called with. Some have the foo
member function, some don't.
You can do this as illustrated by the test program below (built with GCC 4.7.0 or clang 3.1).
The static template function has_void_foo_no_args_const<T>::eval(T const & t)
will invoke t.foo()
if the method void T::foo() const
exists and is public. It will
do nothing if there is no such method. (And of course a compile error will
result if the method is private.)
This solution is adapted and extended from the method-introspecting template I contributed here. You can read that answer for an explanation of how the SNIFAE logic works, and also to see how the technique might be generalised to parameterize the properties of the function-signature you are probing.
#include <iostream>
/*! The template `has_void_foo_no_args_const<T>` exports a
boolean constant `value` that is true iff `T` provides
`void foo() const`
It also provides `static void eval(T const & t)`, which
invokes void `T::foo() const` upon `t` if such a public member
function exists and is a no-op if there is no such member.
*/
template< typename T>
struct has_void_foo_no_args_const
{
/* SFINAE foo-has-correct-sig :) */
template<typename A>
static std::true_type test(void (A::*)() const) {
return std::true_type();
}
/* SFINAE foo-exists :) */
template <typename A>
static decltype(test(&A::foo))
test(decltype(&A::foo),void *) {
/* foo exists. What about sig? */
typedef decltype(test(&A::foo)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0,0)) type;
static const bool value = type::value; /* Which is it? */
/* `eval(T const &,std::true_type)`
delegates to `T::foo()` when `type` == `std::true_type`
*/
static void eval(T const & t, std::true_type) {
t.foo();
}
/* `eval(...)` is a no-op for otherwise unmatched arguments */
static void eval(...){
// This output for demo purposes. Delete
std::cout << "T::foo() not called" << std::endl;
}
/* `eval(T const & t)` delegates to :-
- `eval(t,type()` when `type` == `std::true_type`
- `eval(...)` otherwise
*/
static void eval(T const & t) {
eval(t,type());
}
};
// For testing
struct AA {
void foo() const {
std::cout << "AA::foo() called" << std::endl;
}
};
// For testing
struct BB {
void foo() {
std::cout << "BB::foo() called" << std::endl;
}
};
// For testing
struct CC {
int foo() const {
std::cout << "CC::foo() called" << std::endl;
return 0;
}
};
// This is the desired implementation of `void f(T const& val)`
template<class T>
void f(T const& val) {
has_void_foo_no_args_const<T>::eval(val);
}
int main() {
AA aa;
std::cout << (has_void_foo_no_args_const<AA>::value ?
"AA has void foo() const" : "AA does not have void foo() const")
<< std::endl;
f(aa);
BB bb;
std::cout << (has_void_foo_no_args_const<BB>::value ?
"BB has void foo() const" : "BB does not have void foo() const")
<< std::endl;
f(bb);
CC cc;
std::cout << (has_void_foo_no_args_const<CC>::value ?
"CC has void foo() const" : "CC does not have void foo() const")
<< std::endl;
f(cc);
std::cout << (has_void_foo_no_args_const<double>::value ?
"Double has void foo() const" : "Double does not have void foo() const")
<< std::endl;
f(3.14);
return 0;
}
This program outputs:
AA has void foo() const
AA::foo() called
BB does not have void foo() const
T::foo() not called
CC does not have void foo() const
T::foo() not called
Double does not have void foo() const
T::foo() not called
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With