I have a problem in my application where I'd like to assert that a function application would be rejected by the compiler. Is there a way to check this with SFINAE?
For example, assume that I'd like to validate that std::transform
to a const
range is illegal. Here's what I have so far:
#include <algorithm>
#include <functional>
#include <iostream>
namespace ns
{
using std::transform;
template<typename Iterator1, typename Iterator2, typename UnaryFunction>
struct valid_transform
{
static Iterator1 first1, last1;
static Iterator2 first2;
static UnaryFunction f;
typedef Iterator2 yes_type;
typedef struct {yes_type array[2];} no_type;
static no_type transform(...);
static bool const value = sizeof(transform(first1, last1, first2, f)) == sizeof(yes_type);
};
}
int main()
{
typedef int *iter1;
typedef const int *iter2;
typedef std::negate<int> func;
std::cout << "valid transform compiles: " << ns::valid_transform<iter1,iter1,func>::value << std::endl;
std::cout << "invalid transform compiles: " << ns::valid_transform<iter1,iter2,func>::value << std::endl;
return 0;
}
Unfortunately, my trait rejects both the legal and the illegal cases. The result:
$ g++ valid_transform.cpp
$ ./a.out
valid transform compiles: 0
invalid transform compiles: 0
Your question is similar to SFINAE + sizeof = detect if expression compiles.
Summary of that answer: sizeof
evaluates the type of the expression passed to it, including instantiating a function template, but it does not generate a function call. This is the reason behind Lol4t0's observations that sizeof(std::transform(iter1(), iter1(), iter2(), func()))
compiles even if std::transform(iter1(), iter1(), iter2(), func())
does not.
Your concrete problem can be solved by evaluating the template from Lol4t0's answer for any output range that is to be supplied to std::transform
. However, the general problem of verifying in a template that a function call will compile appears to be impossible to be solved with the sizeof + SFINAE
trick. (it would require a compile-time expression that is derivable from a run-time function call).
You might want to try ConceptGCC to see if this allows you to express the requisite compile-time checking in a more convenient way.
In my answer, I'dd like to focus on problem, how to determine, if given iterator constant:
std::is_const
was mentioned, but it does not work in this case for me (gcc 4.7).
I assume, it is implemeted like
template <typename T>
struct is_const
{
enum {value = false };
};
template <typename T>
struct is_const<const T>
{
enum {value = true };
};
Now, one cannot check reference types with this struct, they would not match specialization, because const T
would match int& const
, that is constant reference to int, not const int&
, that is reference to constant int, and first have no any sense.
Ok, but we are able to determine, if iterator is constant with the follwing struct:
template <typename Iterator>
struct is_iterator_constant
{
typedef char yes_type;
typedef struct{ char _[2];} no_type;
template <typename T>
static no_type test(T&);
template <typename T>
static yes_type test(...);
enum {value = sizeof(test<typename std::iterator_traits<Iterator>::value_type>(*Iterator())) == sizeof(yes_type) };
};
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