c++ 用模板获取函数的参数类型

oyt4ldly  于 2023-07-01  发布在  其他
关注(0)|答案(4)|浏览(103)

假设我有一个具有以下签名的函数:

void foo(std::string const& a, int b, char &c) {
    ...
}

我怎么能做类似param_type<foo, 3>::type的事情来得到type == char呢?
背景资料:
我有一组宏/TMP,它生成一个用于将JSON对象转换为C++值的结构体。我还有一组结构体来表示每一种JSON类型(原语、数组、对象等)。主结构体由X宏定义,实现者必须将参数类型(也用作字段类型),json类型(结构体之一)和键名传递到X宏中以定义字段。
我希望能够将字段类型与参数类型分开,这样我就可以将std::optional<TField>作为结构体的字段类型,并将TField传递给parse方法。这些宏在很多地方都有使用,所以我不想再给X宏添加一个参数。
我尝试使用一个auto变量,但据我所知,像下面这样的东西是不可能的,这就是为什么我想要param_type

auto value;
parse(..., value);
field = value;
ohtdti5x

ohtdti5x1#

你可以创建一个带有部分特化的函数traits:

template <auto func, std::size_t I>
struct param_type;

template <typename Ret, typename... Args, Ret (*func)(Args...), std::size_t I>
struct param_type<func, I>
{
    using type = std::tuple_element_t<I, std::tuple<Args...>>;
};

// C-ellipsis version aka printf-like functions
template <typename Ret, typename... Args, Ret (*func)(Args..., ...), std::size_t I>
struct param_type<func, I>
{
    using type = std::tuple_element_t<I, std::tuple<Args...>>;
};

Demo

brccelvz

brccelvz2#

另一种std::tuple_element方式通过函数声明(仅声明,无需定义)

template <std::size_t Ind, typename R, typename ... Args>
std::tuple_element_t<Ind-1u, std::tuple<std::remove_reference_t<Args>...>>
   pt_helper (R(Args...));

所以param_type简单地变成

template <auto X, std::size_t Ind>
struct param_type
 { using type = decltype( pt_helper<Ind>(X) ); };

观察pt_helper声明中的Ind-1u:你要求param_type返回带有参数3的函数的第三个参数类型:通常,在C/C++世界中,索引从零开始计数。我建议你接受param_type<foo, 2>::typechar;在这种情况下,您必须删除-1
也观察std::remove_reference_t。因为您希望param_type<foo, 3>::typechar(不带引用)而不是char &(带引用)。
无论如何,下面是一个完整的编译示例

#include <tuple>
#include <string>
#include <type_traits>

void foo (std::string const& a, int b, char &c)
 { }

template <std::size_t Ind, typename R, typename ... Args>
std::tuple_element_t<Ind-1u, std::tuple<std::remove_reference_t<Args>...>>
   pt_helper (R(Args...));

template <auto X, std::size_t Ind>
struct param_type
 { using type = decltype( pt_helper<Ind>(X) ); };

int main ()
 {
   using T = typename param_type<foo, 3u>::type;

   static_assert( std::is_same_v<T, char>, "!" );
 }
bqujaahr

bqujaahr3#

在本例中,您可以检查2种类型是否与std::is_same<T,U>::value相同

if (std::is_same<param_type<foo, 3>::type,char>::value) {
    /*stuff*/
}else {
    /*stuff2*/
}

查看更多关于is_same

qybjjes1

qybjjes14#

有点晚了,但看到我自己的完整(免费)函数traits库here(生产级,完整文档-比Boost版本小得多,也更完整)。将其应用于您的代码(演示here):

#include <string>
#include <iostream>

// See https://github.com/HexadigmSystems/FunctionTraits
#include "TypeTraits.h"

void foo(std::string const& a, int b, char &c)
{
    // ...
}

int main()
{
    ////////////////////////////////////////////////
    // Everything in "TypeTraits.h" above declared
    // in this namespace
    ////////////////////////////////////////////////
    using namespace StdExt;
    
    using F = decltype(foo);
    
    ////////////////////////////////////////////////////////////
    // 3rd type in "foo()" (passing 2 as the zero-based index)
    // so yields "char &" (the arg's exact type). Just apply
    // "std::remove_reference_t" as well if you want to remove
    // the "&" (resulting in "char").
    ////////////////////////////////////////////////////////////
    using Arg3InFoo_t = ArgType_t<F, 2>;

    ////////////////////////////////////////////////////////////////////
    // Display above type (its user-friendly name). Alternatively you
    // can use "ArgTypeName_v" instead of "TypeName_v" (former just
    // defers to the latter).
    //
    // Note: Following call to "tcout" (also declared in "TypeTraits.h"
    // above) always resolves to "std::cout" on non-Microsoft platforms
    // and usually "std::wcout" on Microsoft platforms (when compiling
    // for UTF-16 in that environment which is usually the case). You
    // can directly call "std::cout" or "std::wcout" instead if you
    // prefer, assuming you know which platform you're always running
    // on.
    ////////////////////////////////////////////////////////////////////
    tcout << TypeName_v<Arg3InFoo_t>;
    
    return 0;
}

相关问题