c++类型trait来检测是否有函数参数是引用



我需要一个类型trait来检测模板实参的函数形参是否为引用。这段代码运行正常,trait是"is_any_param_reference";触发static_assert, foo的签名由void foo( std::string s, int i)变为void foo( std::string& s, int i)(第一个参数转换为reference)

但是id不能与lambdas一起工作…如果我使用:

,它不会编译
int main()
{ 
    auto foo2 = [](std::string s, int i){ std::cout << s << " " << i << std::endl; };
    some_function( foo2, s, i  );
}

任何想法如何生成的类型特征,也为lambda工作?

谢谢! !

#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
template<typename ... A>
struct any_is_reference : std::false_type {};
template<typename A, typename ... P>
struct any_is_reference<A, P...>: std::conditional_t< std::is_reference<A>::value , std::true_type, any_is_reference<P...> >::type{};
template<typename Sig> struct is_any_param_reference;
template<typename R, typename...Args>
struct is_any_param_reference<R(*)(Args...)>: any_is_reference<Args...>{};
template< typename Sig >
inline constexpr bool is_any_param_reference_v = is_any_param_reference<Sig>::value;
template< typename F , typename ... Args>
void some_function( F f, Args... args)
{
    static_assert(!is_any_param_reference< F >::value, "FUNCTION MUST NOT HAVE PARAMETERS BY REFERENCE");
    
    f(args...);
}
void foo( std::string s, int i)
{
    std::cout << s << " " << i << std::endl;
}
int main()
{
    const std::string s = "Hello";
    int i = 0;
    some_function(foo, s, i);
}
template<typename T> struct is_any_param_reference :
    is_any_param_reference<decltype(&T::operator())>{};
template<typename R, typename...Args>
struct is_any_param_reference<R(*)(Args...)>: any_is_reference<Args...>{};
template<typename T, typename R, typename...Args>
struct is_any_param_reference<R(T::*)(Args...)>: any_is_reference<Args...>{};
template<typename T, typename R, typename...Args>
struct is_any_param_reference<R(T::*)(Args...) const>: any_is_reference<Args...>{};

演示。

主模板假设它的参数是一个提供operator()的类(例如lambda)。然后是普通函数指针和指向成员函数的指针的专门化。主模板有效地委托给最后一个专门化。

一年之后,你可以更容易地使用我的免费开源函数特性库来完成这个任务,这个库(有效地)是一个处理函数特性的单插入头文件(适用于c++ 17和更高版本)。详情请看这里。它是生产级的,并且有完整的文档。我之所以写它,是因为我对处理函数特征的(极少数)其他库不满意,包括Boost的版本(在上面的链接中有更多关于这一点的信息)。然而,这个库是为了每个人的利益,因为它实际上是一个c++扩展,用于处理c++标准本身所缺少的函数特征(对于大多数意图和目的)。

对于您的情况,请检查循环遍历所有函数参数,我在下面的代码中应用了这些参数来处理您的情况。它演示了ForEachArg函数模板,用于迭代其"F"中的所有参数类型。模板arg ("F"可以是任何函数(包括函数指针、函数引用、函子等)。对于在"F"中遇到的每个参数,ForEachArg调用一个回调函数,您将该函数作为唯一的参数传递给ForEachArg。你的回调函数实际上是一个模板,在每次迭代中,它都会接收"F"中的下一个参数;(它在函数及其类型中的从零开始的索引)。因此,您只需在每次迭代中检查当前参数类型,看看它是否是引用,如果是,则返回false以立即退出ForEachArg。因此后一个函数停止迭代并返回"false"它相当于常规"for"中的break语句。循环。在您的例子中,它的false返回值表示所有参数的循环停止,因为您遇到了一个引用。

请注意,如果目标是c++ 20或更高版本(lambda模板仅在c++ 20中可用),则依赖lambda模板来执行其工作,如果目标是c++ 17,则依赖普通函子(与lambda模板相同,但使用普通函子)。lambda模板看起来不那么冗长(所以我个人更喜欢它),但是c++ 17代码也可以在c++ 20和更高版本中工作,所以如果你愿意,你可以总是消除c++ 20代码,只是(完全)依赖于c++ 17代码(在这种情况下不需要检查c++版本,因为代码将在c++ 17 中工作)。

要运行代码,请点击这里:

// Standard C/C++ headers
#include <cstddef>
#include <string>
#include <iostream>
//////////////////////////////////////////////////////
// "FunctionTraits" library. See
// https://github.com/HexadigmSystems/FunctionTraits
//////////////////////////////////////////////////////
#include "TypeTraits.h"
/////////////////////////////////////////////
// Everything in header just above declared
// in this namespace
/////////////////////////////////////////////
using namespace StdExt;
//////////////////////////////////////////////////////
// Following code uses a lambda template which isn't
// available until C++20 (less verbose than the C++17
// code just below so I personally prefer it)
//////////////////////////////////////////////////////
#if CPP20_OR_LATER
    // See https://github.com/HexadigmSystems/FunctionTraits#looping-through-all-function-arguments
    template <TRAITS_FUNCTION_C F>
    constexpr bool is_any_param_reference = !ForEachArg<F>([]<std::size_t I, typename ArgTypeT>() noexcept
                                                          {
                                                               return !std::is_reference_v<ArgTypeT>;
                                                          });
////////////////////////////////////////////////////////
// Lambda templates not supported in C++17 so roll our
// own functor instead (equivalent to lambda template
// above). The following can be used in C++20 or later
// as well if you wish to eliminate the C++20 code
// above and just rely on the following exclusively (in
// C++17 and later)
////////////////////////////////////////////////////////
#elif CPP17
    // Functor identical to lambda template above
    struct HasReferenceArg
    {
        template <std::size_t I, typename ArgTypeT>
        constexpr bool operator()() const noexcept
        {
            return !std::is_reference_v<ArgTypeT>;
        }
    };
    // See https://github.com/HexadigmSystems/FunctionTraits#looping-through-all-function-arguments
    template <typename F>
    constexpr bool is_any_param_reference = !ForEachArg<F>(HasReferenceArg());
#else
    #error "This program is only supported in C++17 or later (an earlier version was detected). Please set the appropriate compiler option to target C++17 or later and try again ("-std=c++17" in GCC, Clang and Intel, or "/std:c++17" in MSFT - later versions also supported of course)"
#endif
template< typename F , typename ... Args>
void some_function( F f, Args... args)
{
    static_assert(!is_any_param_reference<F>, "FUNCTION MUST NOT HAVE PARAMETERS BY REFERENCE");
    
    f(args...);
}
void foo(std::string s, int i)
{
    std::cout << s << " " << i << std::endl;
}
int main()
{
    const std::string s = "Hello";
    int i = 0;
    some_function(foo, s, i);
    auto foo2 = [](std::string s, int i){ std::cout << s << " " << i << std::endl; };
    some_function(foo2, s, i);
    return 0;
}

最新更新