Reading the Chapter 22 of C++ templates, Second Edition, I try to understand the implementation of the EqualityComparable trait. But I cannot understand how the compiler decide to activate the fallback or not.
In addition to this there are two function that have only declared but the program compiles and runs. This is strange to me.
Here is the code.
The header file IsEqualityComparable.hpp
#include <utility> // for declval()
#include <type_traits> // for true_type and false_type
template<typename T>
class IsEqualityComparable
{
private:
// test convertibility of == and ! == to bool:
static void* conv(bool); // to check convertibility to bool
template<typename U>
static std::true_type test(decltype(conv(std::declval<U const&>() ==
std::declval<U const&>())),
decltype(conv(!(std::declval<U const&>() ==
std::declval<U const&>())))
);
// fallback:
template<typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(nullptr,
nullptr))::value;
};
The source file is the following
#include <iostream>
#include <exception>
#include <utility>
#include <functional>
#include "isequalitycomparable.hpp"
template<typename T,
bool EqComparable = IsEqualityComparable<T>::value>
struct TryEquals
{
static bool equals(T const& x1, T const& x2) {
std:: cout << "IsEqualityComparable equals::"<<std::endl;
return x1 == x2;
}
};
class NotEqualityComparable : public std::exception
{
};
template<typename T>
struct TryEquals<T, false>
{
static bool equals(T const& x1, T const& x2) {
std:: cout << "Throw::"<<std::endl;
throw NotEqualityComparable();
}
};
void foo(int)
{
}
void bar(int)
{
}
class A
{
public:
A() = default;
friend bool operator ==(A a1 , A a2)
{
return true;
}
};
int main()
{
std:: cout << "Enter" << std::endl;
std::function<void(int)> f = foo;
std::function<void(int)> f2 = f;
std:: cout << "Enter" << std::endl;
//std:: cout << "Check::"<<
//TryEquals<std::function<void(int)>>::equals(f,f2) << std::endl;
A a1;
A a2;
std:: cout << "Check::"<< TryEquals<A>::equals(a1,a2) << std::endl;
return 0;
}
The
TryEquals<std::function<void(int)>>::equals(f,f2)
throws an exception because the operator == is not implemented but
TryEquals<A>::equals(a1,a2)
returns 1 because the class A has an operator ==.
In this point I need help to understand how the conv and test work.
Moreover how does the
static constexpr bool value = decltype(test<T>(nullptr,
nullptr))::value
works?
I confused with this expression
decltype(test<T>(nullptr,nullptr))::value.
The functions do not need to be defined, because they are never actually called.
decltype is an Unevaluated context where it figures out the return type of the function, but never tries to compute a return value.
In this case it is combined with sfinae, so that if decltype cannot figure out the return type of == (probably because the operator doesn't exist) that overload of test will be ignored. And then the test(...) will be selected instead.
This uses the fact that ... is the absolute worst match for a parameter type, so it will be used only if there are no other overloads available (thus "fallback").
And by the way, std::declval is never defined either.
Related
Consider this (rather) simple example:
#include <iostream>
struct Out {
int value;
};
template<class Sink> decltype(auto) operator<<(Sink &&s, Out const &out) {
return out.value > 0? s << out.value : s;
}
struct In {
std::ostream &sink;
template<typename T> In &operator<<(T const &t) {
return sink << t, *this;
}
};
int main() {
In in{std::cout};
in << (1 << Out{3}) << '\n'; // Ok
in << Out{42} << '\n';
// error: use of overloaded operator '<<' is ambiguous
// (with operand types 'In' and 'Out')
}
Can such an ambiguity be addressed? We have two classes, each one defines such an operator overload to forward it to its internal type (classes are designed independently by two different people, and another person tries to use them in the same application.) I don't see a way this could be reformulated in terms of other operators, say, it's no use here to give up on A's friend operator<< and try to convert A to int; and to use some kind of complicated SFINAE to rule out some overloads still doesn't look helpful.
You might create additional overloads which would be the better match:
decltype(auto) operator<<(In& in, Out const &out) {
return in.operator<<(out);
}
decltype(auto) operator<<(In&& in, Out const &out) {
return in.operator<<(out);
}
Some semi-oldschool SFINAE seemingly does the trick, in the sense that it is now accepted by both gcc and clang (and they both print "8" and "42" equally):
#include <iostream>
#include <utility>
template<typename S, typename T> class can_shl {
using Left = char;
struct Right { char c[2]; };
template<typename U> static constexpr decltype(
std::declval<S>() << std::declval<U>(), Left{})
has_it(U &&);
static constexpr Right has_it(...);
public:
static constexpr bool value = sizeof(has_it(std::declval<T>())) == 1;
};
struct Out {
int value;
};
template<class Sink> auto operator<<(Sink &&s, Out const &out)
-> std::enable_if_t<!can_shl<Sink, Out>::value, decltype(out.value > 0? s << out.value : s)>
{
return out.value > 0? s << out.value : s;
}
struct In {
std::ostream &sink;
template<typename T> In &operator<<(T const &t) {
return sink << t, *this;
}
};
int main() {
In in{std::cout};
in << (1 << Out{3}) << '\n'; // Ok
in << Out{42} << '\n';
}
Personally I don't feel quite confident with that "only allow this in case it does not compile on its own" approach (is this perchance a (static) UB? what would I say if I were a C++ standard?)
TL;DR
A couple of templatized and overloaded non-templatized member functions in a non-templatized class should all end up routing through the same member function to perform the actual work. All the overloads and templatizations are done to convert the "data buffer" into gsl::span<std::byte> type (essentially a close relative to std::array<std::byte, N> from the Guidelines Support Library)
Wall of code
#include <array>
#include <cstdlib>
#include <iostream>
#pragma warning(push)
#pragma warning(disable: 4996)
#include <gsl.h>
#pragma warning(pop)
// convert PoD into "memory buffer" for physical I/O
// ignore endianness and padding/alignments for this example
template<class T> gsl::span<std::byte> byte_span(T& _x) {
return { reinterpret_cast<std::byte*>(&_x), sizeof(T) };
}
// implementation of physical I/O (not a functor, but tempting)
struct A {
enum class E1 : uint8_t { v1 = 10, v2, v3, v4 };
bool f(uint8_t _i1, gsl::span<std::byte> _buf = {}); // a - "in the end" they all call here
bool f(E1 _i1, gsl::span<std::byte> _buf = {}); // b
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(uint8_t _i1, T& _val); // c
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(E1 _i1, T& _val); // d
};
bool A::f(uint8_t _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() uint8_t{" << (int)_i1 << "} with " << _buf.size() << " elements\n";
return true;
}
bool A::f(E1 _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() E1{" << (int)_i1 << "} with " << _buf.size() << " elements\n\t";
return f((uint8_t)_i1, _buf);
}
template<class T, typename>
bool A::f(uint8_t _i1, T& _val)
{
std::cout << "template uint8_t\n\t";
return f(_i1, byte_span(_val));
}
template<class T, typename>
bool A::f(E1 _i1, T& _val)
{
std::cout << "template E1\n\t";
return f(_i1, byte_span(_val));
}
int main(){
A a = {};
std::array<std::byte, 1> buf;
long i = 2;
// regular function overloads
a.f(1, buf); // should call (a)
a.f(A::E1::v1, buf); // should call (b)
// template overloads
a.f(2, i); // should call (c)
a.f(A::E1::v2, i); // should call (d)
struct S { short i; };
// issue
//S s;
//a.f(3, s); // should call (c)
//a.f(A::E1::v3, s); // should call (d)
//// bonus - should use non-template overrides
//S sv[2] = {};
//a.f(5, sv); // should call (a)
//a.f(A::E1::v1, sv); // should call (b)
}
Details
The struct S is a PoD and it is tempting to change the template's enable_if to use the std::is_trivial or std::is_standard_layout. Unfortunately both these solutions "grab too much" and end up matching std::array (even if they do fix the compilation error of the //issue block).
The solution I have right now looks like a dead-end as my gut feeling is to start adding more template parameters and it seems to be getting very hairy very soon :(
Question
My goal is to achieve the following: use the class A's bool f() member function without too much syntactic overhead for any PoD (possibly including C arrays - see "bonus" in code) as shown in the body of main() and no runtime function call overhead for types that are auto-convertible to gsl::span (like std::array and std::vector).
Ideally...
I'd like to have a single templatized function per first parameter (either E1 or uint8_t) with multiple specializations listed outside the class's body to further reduce the perceived code clutter in the class's declaration and I can't figure out the way to properly do that. Something like the following (illegal C++ code below!):
struct A {
// ...
template<typename T> bool f(uint8_t _i1, T& _val);
template<typename T> bool f(E1 _i1, T& _val);
};
template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(E1 /*...*/}
template<> bool f<is_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_like_gsl_span<T>>(E1 /*...*/}
If this is not achievable I'd like to know why.
I'm on MSVC 2017 with C++17 enabled.
The first answer was a little wrong, for some reasons, I was totally confused by gsl. I thought it is something super specific. I am not using Guideline Support Library, though I have seen it and it looks good.
Fixed the code to properly work with gsl::span type.
struct A {
enum class E1 : uint8_t { v1 = 10, v2, v3, v4 };
private:
template <typename T>
static auto make_span(T& _x) ->
typename std::enable_if<std::is_convertible<T&, gsl::span<std::byte>>::value,
gsl::span<std::byte>>::type {
std::cout << "conversion" << std::endl;
return _x;
}
template <typename T>
static auto make_span(T& _x) ->
typename std::enable_if<!std::is_convertible<T&, gsl::span<std::byte>>::value,
gsl::span<std::byte>>::type {
std::cout << "cast" << std::endl;
return {reinterpret_cast<std::byte*>(&_x), sizeof(T)};
}
public:
template <typename T, typename U>
bool f(T _i, U& _buf) {
static_assert(
std::is_convertible<U&, gsl::span<std::byte>>::value || std::is_trivial<U>::value,
"The object must be either convertible to gsl::span<std::byte> or be of a trivial type");
const auto i = static_cast<uint8_t>(_i);
const auto span = make_span(_buf);
std::cout << "f() uint8_t{" << (int)i << "} with " << span.size() << " elements\n";
return true;
}
};
I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?
I like using SFINAE to check boolean conditions.
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
The list is only accepted when M is smaller than N, which means that the initializer list has not too many elements.
The syntax char(*)[C] means: Pointer to an array with element type char and size C. If C is false (0 here), then we get the invalid type char(*)[0], pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.
Expressed with boost::enable_if, that looks like this
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
In practice, i often find the ability to check conditions a useful ability.
Heres one example (from here):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
When IsClassT<int>::Yes is evaluated, 0 cannot be converted to int int::* because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the ... form which returns Two, and thus evaluates to false, int is not a class type.
In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:
Pick a function overload depending on traits
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
Here is a live example: http://ideone.com/dHhyHE
I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html
Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.
Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.
Here's another (late) SFINAE example, based on Greg Rogers's answer:
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
In this way, you can check the value's value to see whether T is a class or not:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
Examples provided by other answers seems to me more complicated than needed.
Here is the slightly easier to understand example from cppreference :
#include <iostream>
// this overload is always in the set of overloads
// ellipsis parameter has the lowest ranking for overload resolution
void test(...)
{
std::cout << "Catch-all overload called\n";
}
// this overload is added to the set of overloads if
// C is a reference-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)(c.*f)(), void())
{
std::cout << "Reference overload called\n";
}
// this overload is added to the set of overloads if
// C is a pointer-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)((c->*f)()), void())
{
std::cout << "Pointer overload called\n";
}
struct X { void f() {} };
int main(){
X x;
test( x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Output:
Reference overload called
Pointer overload called
Catch-all overload called
As you can see, in the third call of test, substitution fails without errors.
C++17 will probably provide a generic means to query for features. See N4502 for details, but as a self-contained example consider the following.
This part is the constant part, put it in a header.
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
The following example, taken from N4502, shows the usage:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations, this one is fairly simple: a reduced set of tools (void_t and detect) suffices. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
Here is a live example, which includes portability tweaks for GCC pre 5.1.
Here is one good article of SFINAE: An introduction to C++'s SFINAE concept: compile-time introspection of a class member.
Summary it as following:
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }
// The sink-hole.
void f(...) { }
f(1); // Calls void f(...) { }
template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.
template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return obj.serialize();
}
template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return to_string(obj);
}
declval is an utility that gives you a "fake reference" to an object of a type that couldn't be easily construct. declval is really handy for our SFINAE constructions.
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
The following code uses SFINAE to let compiler select an overload based on whether a type has certain method or not:
#include <iostream>
template<typename T>
void do_something(const T& value, decltype(value.get_int()) = 0) {
std::cout << "Int: " << value.get_int() << std::endl;
}
template<typename T>
void do_something(const T& value, decltype(value.get_float()) = 0) {
std::cout << "Float: " << value.get_float() << std::endl;
}
struct FloatItem {
float get_float() const {
return 1.0f;
}
};
struct IntItem {
int get_int() const {
return -1;
}
};
struct UniversalItem : public IntItem, public FloatItem {};
int main() {
do_something(FloatItem{});
do_something(IntItem{});
// the following fails because template substitution
// leads to ambiguity
// do_something(UniversalItem{});
return 0;
}
Output:
Float: 1
Int: -1
Here, I am using template function overloading (not directly SFINAE) to determine whether a pointer is a function or member class pointer: (Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?)
https://godbolt.org/z/c2NmzR
#include<iostream>
template<typename Return, typename... Args>
constexpr bool is_function_pointer(Return(*pointer)(Args...)) {
return true;
}
template<typename Return, typename ClassType, typename... Args>
constexpr bool is_function_pointer(Return(ClassType::*pointer)(Args...)) {
return true;
}
template<typename... Args>
constexpr bool is_function_pointer(Args...) {
return false;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main(void) {
int* var;
std::cout << std::boolalpha;
std::cout << "0. " << is_function_pointer(var) << std::endl;
std::cout << "1. " << is_function_pointer(fun_void_void) << std::endl;
std::cout << "2. " << is_function_pointer(fun_void_double) << std::endl;
std::cout << "3. " << is_function_pointer(fun_double_double) << std::endl;
std::cout << "4. " << is_function_pointer(&test_debugger::var) << std::endl;
return 0;
}
Prints
0. false
1. true
2. true
3. true
4. true
As the code is, it could (depending on the compiler "good" will) generate a run time call to a function which will return true or false. If you would like to force the is_function_pointer(var) to evaluate at compile type (no function calls performed at run time), you can use the constexpr variable trick:
constexpr bool ispointer = is_function_pointer(var);
std::cout << "ispointer " << ispointer << std::endl;
By the C++ standard, all constexpr variables are guaranteed to be evaluated at compile time (Computing length of a C string at compile time. Is this really a constexpr?).
I am using the C++03 method to detect the presence of a function at compile time. I have to use this method rather than the void_t method even though I'm using C++14 because I have to support GCC 4.9, and it errors when using the void_t method (strangely only Ubuntu 14's GCC 4.9 has this problem, not Fedora's, but it's fixed across the board in GCC5+ AFAICT).
Specifically I am checking for the presence of operator<<(std::ostream&, const T&) so that I can have a pretty print function that takes any type. When the function gets called, you get the regular ostream output if the type supports it, and you get a fallback message about there being no implementation when the operator is not defined. Code at the bottom.
This has worked pretty well for me so far, until I ran into a type defined by a 3rd party library that I can't change. The type has implicit conversion operators to both bool and float. This means when the SFINAE check is done to see if s << t is valid I get a compiler error because s << t is ambiguous. In this case I'd prefer for it to just report there is no implementation like normal, rather than try to pick an implicit conversion. Is there a way to change the SFINAE check to make this possible? I have checked and the void_t method with GCC5 appears to do what I want (commented out in the code below), but I can't use it yet for the aforementioned reasons.
Test case:
#include <iostream>
#include <typeinfo>
#include <type_traits>
namespace detail {
namespace has_ostream_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
template<typename T> any_t( T const& );
};
no operator<<( std::ostream const&, any_t const& );
yes& test( std::ostream& );
no test( no );
template<typename T>
struct has_ostream_operator {
static std::ostream &s;
static T const &t;
// compiler complains that test(s << t) is ambiguous
// for Foo
static bool const value = sizeof( test(s << T(t)) ) == sizeof( yes );
};
}
template<typename T>
struct has_ostream_operator :
has_ostream_operator_impl::has_ostream_operator<T> {
};
// template<class, class = std::void_t<>>
// struct has_ostream_operator : std::false_type {};
// template<class T>
// struct has_ostream_operator<
// T,
// std::void_t<
// decltype(std::declval<std::ostream&>() << std::declval<const T&>())>>
// : std::true_type {};
}
template<class X>
std::enable_if_t<
detail::has_ostream_operator<X>::value
&& !std::is_pointer<X>::value>
prettyPrint(std::ostream& o, const X& x)
{
o << x;
}
template<class X>
std::enable_if_t<
!detail::has_ostream_operator<X>::value
&& !std::is_pointer<X>::value>
prettyPrint(std::ostream& o, const X& x)
{
o << typeid(x).name()
<< " (no ostream operator<< implementation)";
}
template<class X>
void prettyPrint(std::ostream& o, const X* x)
{
o << "*{";
if(x) {
prettyPrint(o, *x);
} else {
o << "NULL";
}
o << "}";
}
struct Foo {
operator float() const {
return 0;
}
operator bool() const {
return false;
}
};
struct Bar {};
int main()
{
Bar x;
Foo y;
prettyPrint(std::cout, 6); // works fine
std::cout << std::endl;
prettyPrint(std::cout, Bar()); // works fine
std::cout << std::endl;
prettyPrint(std::cout, x); // works fine
std::cout << std::endl;
prettyPrint(std::cout, &x); // works fine
std::cout << std::endl;
// prettyPrint(std::cout, y); // compiler error
std::cout << std::endl;
return 0;
}
Well, you don't have to use void_t (which is syntax sugar, anyway). Bog-standard expression SFINAE is supported by GCC 4.9:
template <typename, typename = void>
struct has_ostream_operator : std::false_type {};
template <typename T>
struct has_ostream_operator<T, decltype(void(std::declval<std::ostream&>() << std::declval<const T&>()))>
: std::true_type {};
works fine on Wandbox's GCC 4.9.
Is there a way, using SFINAE, to detect whether a free function is overloaded for a given class?
Basically, I’ve got the following solution:
struct has_no_f { };
struct has_f { };
void f(has_f const& x) { }
template <typename T>
enable_if<has_function<T, f>::value, int>::type call(T const&) {
std::cout << "has f" << std::endl;
}
template <typename T>
disable_if<has_function<T, f>::value, int>::type call(T const&) {
std::cout << "has no f" << std::endl;
}
int main() {
call(has_no_f()); // "has no f"
call(has_f()); // "has f"
}
Simply overloading call doesn’t work since there are actually a lot of foo and bar types and the call function has no knowledge of them (basically call is inside a and the users supply their own types).
I cannot use C++0x, and I need a working solution for all modern compilers.
Note: the solution to a similar question unfortunately doesn’t work here.
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <functional>
#include <type_traits>
struct X {};
struct Y {};
__int8 f(X x) { return 0; }
__int16 f(...) { return 0; }
template <typename T> typename std::enable_if<sizeof(f(T())) == sizeof(__int8), int>::type call(T const& t) {
std::cout << "In call with f available";
f(t);
return 0;
}
template <typename T> typename std::enable_if<sizeof(f(T())) == sizeof(__int16), int>::type call(T const& t) {
std::cout << "In call without f available";
return 0;
}
int main() {
Y y; X x;
call(y);
call(x);
}
A quick modification of the return types of f() yields the traditional SFINAE solution.
If boost is allowed, the following code might meet your purpose:
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;
// user code
struct A {};
static void f( A const& ) {}
struct B {};
// code for has_f
static void f(...); // this function has to be a free standing one
template< class T >
struct has_f {
template< class U >
static char deduce( U(&)( T const& ) );
template< class U, class V >
static typename disable_if_c< is_same< V, T >::value, char(&)[2] >::type
deduce( U(&)( V const& ) );
static char (&deduce( ... ))[2];
static bool const value = (1 == sizeof deduce( f ));
};
int main()
{
cout<< has_f<A>::value <<endl;
cout<< has_f<B>::value <<endl;
}
However, there are severe restrictions.
The code assumes that all the user functions have the signature ( T const& ),
so ( T ) isn't allowed.
The function void f(...) in the above seems to need to be a free standing
function.
If the compiler enforces two phase look-up as expected normally, probably
all the user functions have to appear before the definition of has_f class
template.
Honestly, I'm not confident of the usefulness of the code, but anyway I hope
this helps.