【问题标题】:Why std::is_function<T> is causing compilation error?为什么 std::is_function<T> 会导致编译错误?
【发布时间】:2013-11-20 18:38:09
【问题描述】:

您好,我试图在 C++11 中实现类似 C++ 概念的功能 (C++14)。这个想法只是为std::for_each() 算法编写包装函数,我只是检查第三个参数是否是函数。所以我编写了以下代码,但是我无法按应有的方式编译它。我正在使用带有 gcc4.8.1 的 Ubuntu12.04。

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;
}

我收到以下编译错误,从中我可以看到 static_assert() 失败,这是不正确的,因为 display 是有效函数。

编译错误

test_3.cpp:在“void check_valid_function(T&) [with T = void (*)(int&)]”的实例化中: test_3.cpp:27:26: 函数 my_for_each(InputIterator, InputIterator, Function) [with InputIterator = __gnu_cxx::__normal_iterator >;函数 = void (*)(int&)]’ test_3.cpp:35:50:从这里需要 test_3.cpp:19:3: 错误:静态断言失败:不是有效函数 static_assert(Is_valid_function(a), "不是有效函数"); ^

但是,如果我对另一个 type_traits 函数做同样的事情,我会得到以下正确且预期的错误。

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;
}

编译器输出

test_2.cpp:在“void f(T&) [with T = int]”的实例化中: test_2.cpp:19:6: 从这里需要 test_2.cpp:11:3:错误:静态断言失败:非浮点类型数据 static_assert(Is_floating_point(a), "非浮点型数据"); ^

问题

所以,我想了解为什么我的第一个程序无法正常运行,我的代码/理解中是否存在错误或其他问题。我希望以上数据足以理解我的问题。但是,如果有人想要一些额外的数据,请告诉我。

【问题讨论】:

    标签: c++ c++11 template-meta-programming static-assert


    【解决方案1】:

    问题就在这里:

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

    通过以下方式调用:

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

    这推导出Functionmy_for_each)是一个函数指针;对于

    void  display(int& val)
    

    推导出的类型是void(*)(int&amp;)。然而,类型特征 std::is_function 会检查传递的类型是否是 函数类型,而不是 函数指针 类型。


    一种解决方法是移除指针:

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

    但是,正如 clang++ 所揭示的,这还不够:

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

    a 作为函数参数(即使 check_valid_functionconstexpr!)不是编译时常量,因此它可能不会出现在常量表达式中(在函数内部身体)。因此,Is_valid_function(a) 可能不会显示为对static_assert 的检查。可能可以使用类似于declval 的内容,例如

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

    但不幸的是,declval 不是 constexpr,我不知道如何写一个constexpr 版本。所以,你可以传递一个指针:

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

    为此,你需要重写Is_valid_function如下:

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

    注意:这里传递的参数是一个指向函数指针的指针,但是参数T*推导出T是一个指针到一个函数,和以前一样(因此签名的变化)。如果您选择此解决方案,您可能希望在函数名称中体现这一点。


    其他问题:

    1. 标准库算法依赖 ADL

      return for_each(first, last, f) ;
      

      据我所知,这依赖于 ADL。但是迭代器(和函数)不需要在命名空间std(即使是vector::iterator等),所以你不应该依赖ADL:

      return std::for_each(first, last, f);
      
    2. 对不需要修改参数的函数使用非常量引用,例如

      constexpr   bool  Is_valid_function(T& a)
      

      如果您不需要修改参数,您应该通过值或常量引用传递它,例如

      constexpr   bool  Is_valid_function(T const& a)
      
    3. “错误检查” 如果此代码仅用于教育目的,这不是问题。但是,在尝试检查参数是否对标准库算法有效时,检查传递的参数是否为函数类型是“错误检查”。您应该检查f(*first) 是否格式正确。这允许函数对象并检查参数类型是否为“有效”。

    【讨论】:

    • 非常感谢 DyP 的回复。我将等待您对此的进一步回应。顺便说一句,我应该注意到推导类型为 void(*)(int&) 的编译器消息,它是函数指针而不是函数类型......
    • @mantosh4u 已经更新了我的答案 ;) 但是 clang++ 揭示了另一个问题,请稍等..
    • 是的,这段代码仅用于教育目的。而且我知道应该使用 std:: 命名空间。我只是错过了那部分......但是我完全明白了你试图向我解释的内容......非常感谢你,我会再次等待你的进一步回复......
    • @mantosh4u 好的,现在它可以同时编译 g++4.8.1 和早期的 clang++3.4
    • template&lt;class T&gt; constexpr add_reference_t&lt;T&gt; declval(); 应该可以工作
    猜你喜欢
    • 1970-01-01
    • 2015-07-23
    • 2011-06-09
    • 2013-02-01
    • 2019-08-30
    • 2014-05-23
    • 1970-01-01
    • 2018-04-08
    • 2020-04-26
    相关资源
    最近更新 更多