Question

Hi I was trying to implement a C++ concept-like feature (C++14) in C++11. The idea is just to write the wrapper function for std::for_each() algorithm where I just check whether the 3rd argument is a function or not. So I wrote the following code, however I am not able to compile it as it should be. I am using Ubuntu12.04 with gcc4.8.1.

test_1.cpp

#include<type_traits>
#include<iostream>
#include<vector>
#include<algorithm>


void  display(int& val) {
    std::cout <<val<<std::endl;
}

template<typename T>
constexpr   bool  Is_valid_function(T& a)  {
        return std::is_function<T>::value; 
}


template<typename T>
void  check_valid_function(T& a)  {
        static_assert(Is_valid_function(a), "Not The Valid Function");
}



template <class InputIterator, class Function>
Function my_for_each(InputIterator first, InputIterator last, Function f) {
/* Concept Like Implementation to just check whether f is function or not */
            check_valid_function(f);
            return for_each(first, last, f) ;
}


void learn_static_assert_and_typetraits(void)
{
        std::vector<int> vec_x{1,2,3,4,5};
        my_for_each(vec_x.begin(), vec_x.end(), display);
    }


int main(int argc, const char* argv[]) {
        learn_static_assert_and_typetraits();
        return 0;
}

I am getting the following compilation error from which I can see that the static_assert() fails which is not correct as display is valid function.

Compilation Error

test_3.cpp: In instantiation of ‘void check_valid_function(T&) [with T = void (*)(int&)]’:
test_3.cpp:27:26:   required from ‘Function my_for_each(InputIterator, InputIterator, Function) [with InputIterator = __gnu_cxx::__normal_iterator >; Function = void (*)(int&)]’
test_3.cpp:35:50:   required from here
test_3.cpp:19:3: error: static assertion failed: Not The Valid Function
   static_assert(Is_valid_function(a), "Not The Valid Function");
   ^

However if I do the same thing for the other type_traits function, I am getting the following error which is correct and expected.

test_2.cpp

#include<type_traits>

template<typename T>
constexpr   bool Is_floating_point(T& a) {
        return std::is_floating_point<T>::value; 
}


template<typename T>
void  f(T& a)  {
        static_assert(Is_floating_point(a), "Non-Float Type  Data");
}


void learn_static_assert_and_typetraits(void) {
        float y{10.0};
        f(y);
        int x{100};
        f(x);
}


int main(int argc, const char* argv[]) {
        learn_static_assert_and_typetraits();
        return 0;
}

Compiler Output

test_2.cpp: In instantiation of ‘void f(T&) [with T = int]’:
test_2.cpp:19:6:   required from here
test_2.cpp:11:3: error: static assertion failed: Non-Float Type  Data
   static_assert(Is_floating_point(a), "Non-Float Type  Data");
   ^

Question

So, I wanted to understand why my first program is not working as it should be, whether there is bug in my code/understanding or it is something else. I hope the above data would be sufficient to understand my question. However if anyone wants some additional data, please let me know.

Was it helpful?

Solution

The issue is here:

template <class InputIterator, class Function>
Function my_for_each(InputIterator first, InputIterator last, Function f)

invoked via:

my_for_each(vec_x.begin(), vec_x.end(), display);

This deduces Function (of my_for_each) to be a function pointer; for

void  display(int& val)

the deduced type is void(*)(int&). The type trait std::is_function however checks if the passed type is a function type, not a function pointer type.


One solution is to remove the pointer:

template<typename T>
constexpr   bool  Is_valid_function(T& a)  {
        return std::is_function<typename std::remove_pointer<T>::type>::value; 
}

But, as clang++ reveals, this still isn't sufficient:

template<typename T>
void  check_valid_function(T& a)  {
        static_assert(Is_valid_function(a), "Not The Valid Function");
}

a as a function parameter (even if check_valid_function was constexpr!) is not a compile-time constant, therefore it may not appear in a constant expression (inside the function body). Hence, Is_valid_function(a) may not appear as the check for the static_assert. It might be possible to use something similar to declval, e.g.

static_assert(Is_valid_function( declval<T&>() ), "Not The Valid Function");

but unfortunately, declval is not constexpr, and I don't know how to write a constexpr version. So, you could pass a pointer instead:

static_assert(Is_valid_function(static_cast<T*>(nullptr)),
              "Not a valid function");

For this, you need to rewrite Is_valid_function as follows:

template<typename T>
constexpr   bool  Is_valid_function(T*)  {
        return std::is_function<typename std::remove_pointer<T>::type>::value; 
}

Note: the passed argument here is a pointer to a pointer to a function, but the parameter T* deduced T to be a pointer to a function, as before (hence the change in the signature). You might want to reflect that in the function name, if you choose this solution.


Other issues:

  1. Relying on ADL for Standard Library algorithms

    return for_each(first, last, f) ;
    

    As far as I can see, this relies on ADL. But the iterators (and the function) are not required to be in namespace std (even for vector::iterator etc.), so you shouldn't rely on ADL:

    return std::for_each(first, last, f);
    
  2. Use of non-const refs for functions that don't need to modify their arguments, e.g.

    constexpr   bool  Is_valid_function(T& a)
    

    If you don't need to modify an argument, you should either pass it by value or by const reference, e.g.

    constexpr   bool  Is_valid_function(T const& a)
    
  3. "Wrong check" If this code is just for educational purposes, this isn't an issue. However, the check if the passed argument is of a function type is the "wrong check" when trying to check if the argument valid for a Standard Library algorithm. You should rather check whether f(*first) is well-formed. This allows for function objects and checks if the argument type is "valid".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top