Search code examples
c++functionc++14template-meta-programmingside-effects

Type traits to detect if a callable has side effects?


My question is simple to ask, but I guess, difficult to answer.

In C++14, is there a way to test if a callable (function, function member, lambda function, std::function etc...) has a side effect or not?

If so, how would a type traits:

template <class T>
struct has_side_effects;

would be like?

I am ok with a trait that would return false positive (says that a function has side effects while it does not), but not ok with a trait that would return false negative (says that a function has no side effects while it does).

For example, I would like the trait:

auto comparator = [](const auto& x, const auto& y){return y > x;};
bool result = std::has_side_effects<decltype(comparator)>::value;

to return false.


Solution

  • As stated, template<class T> using has_side_effects = std::true_type; solves most of your problem. Simply state that everything has side effects, and ship it. False positives, but no false negatives!

    In general, non-trivial properties of an algorithm coded in a Turing-complete system cannot be computed. Doing so is equivalent to the halting problem. So the problem cannot be solved in general.

    In specific cases, C++ offers basically zero reflection over the contents of an algorithm. At best it offers some reflection over the interface of an algorithm, but the interface of an algorithm/function does not contain information about the purity of the algorithm.

    The closest you can get is "can it be invoked within a constexpr context".

    In a concrete case:

    auto comparator = [](const auto& x, const auto& y){return y > x;};
    bool result = std::has_side_effects<decltype(comparator)>::value;
    

    result would have to be true, as:

    struct evil {
      static int count() { static int r = 0; return ++r; }
      friend bool operator<( evil lhs, evil rhs ) { count(); return false; }
    };
    

    then comparator(evil{}, evil{}) has a side effect. Having it return false when passed comparator is simply incorrect.