Search code examples
c++sfinaetype-traitsc++-concepts

How to check at compile time that an expression is illegal?


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

Solution

  • 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.