根据 C++ 模板函数的参数类型解析为不同类型

Resolving to different types based on the argument types of a c++ template function

我正在做一些元编程,我 运行 遇到了以下问题:

我有一个class接受一个模板参数TT可以被认为是一个具有任意签名的函数。 class 一个成员变量 V,如果 T 没有参数或者第一个参数不是 std::tuple,它的类型应该是 std::tuple<>。如果第一个参数是 std::tupleV 应该与第一个参数具有相同的类型。

示例:

 void f() // Should resolve to std::tuple<>
 void f(int) // Should resolve to std::tuple<>
 void f(std::tuple<int, float>) // Should resolve to std::tuple<int, float>
 void f(std::tuple<float>, int) // Should resolve to std::tuple<float>

我一直在尝试类似的方法,但没有成功。因为它在对参数自由函数的第一个参数进行索引时失败,尽管有可用的选项,但没有选择任何其他选项。我正在使用 MSVC 2019 16.8.4

#include <functional>
#include <concepts>
namespace detail
{
    template<typename... ArgTs>
    struct HasArgs : public std::conditional<(sizeof... (ArgTs) > 0), std::true_type, std::false_type>::type {};
}

//!
//! Provides argument function information
//! Based on: 
//! 
template<typename T>
class FunctionTraits;

template<typename R, typename... Args>
class FunctionTraits<R(Args...)>
{
public:
    static const size_t arg_count = sizeof...(Args);
    using HasArguments = detail::HasArgs<Args...>;

    using ReturnType = R;
    using ArgTypes = std::tuple<Args...>;

    template <size_t i>
    struct arg
    {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
    };
};

namespace detail
{
template <typename T>
struct is_tuple : std::false_type {};

template <typename... Args>
struct is_tuple<std::tuple<Args...>>: std::true_type {};
}

template <typename T>
concept is_tuple = requires() { detail::is_tuple<T>::value; };


class TestMemberFunctions
{
public:
    static int test_f1(std::tuple<int, float>, int)
    {
        return 0;
    }

    static int test_f2(int)
    {
        return 0;
    }
    static int test_f3()
    {
        return 0;
    }

};

template <typename CreateT> requires (!FunctionTraits<CreateT>::HasArguments::value)
std::tuple<> TypeDeductionDummyFunction();


template <typename CreateT> requires FunctionTraits<CreateT>::HasArguments::value
auto TypeDeductionDummyFunction() -> std::conditional<is_tuple<typename FunctionTraits<CreateT>::template arg<0>::type>,
                                                                                                typename FunctionTraits<CreateT>::template arg<0>::type,
                                                                                                std::tuple<>>;

template <typename T>
class SampleClass
{
    decltype(TypeDeductionDummyFunction<T>()) m_member;
};

SampleClass<decltype(TestMemberFunctions::test_f1)> c1; 
SampleClass<decltype(TestMemberFunctions::test_f2)> c2; 
SampleClass<decltype(TestMemberFunctions::test_f3)> c3; 


大概是这样的:

template <typename T> struct ExtractFirstTuple;

template <typename R>
struct ExtractFirstTuple<R()> {
    using type = std::tuple<>;
};

template <typename R, typename... Ts, typename... Args>
struct ExtractFirstTuple<R(std::tuple<Ts...>, Args...)> {
    using type = std::tuple<Ts...>;
};

template <typename R, typename First, typename... Args>
struct ExtractFirstTuple<R(First, Args...)> {
    using type = std::tuple<>;
};

Demo

尝试从更原始的操作中构建您想要的东西。

template<typename T, std::size_t N>
struct FunctionArgument {
  static constexpr bool exists = false;
};

template<typename R, typename A0, typename... Args>
struct FunctionArgument<R(A0, Args...), 0>{
  using type=A0;
  static constexpr bool exists = true;
};

template<typename R, typename A0, typename... Args, std::size_t N>
struct FunctionArgument<R(A0, Args...), N>:
  FunctionArgument<R(Args...), N-1>
{};

template<class Sig, std::size_t N>
using FuncArg_type = typename FunctionArgument<Sig, N>::type;
template<class Sig, std::size_t N>
constexpr bool FuncArg_exists = FunctionArgument<Sig, N>::exists;

template<class Sig, class Otherwise>
using FirstArgIfExists = 
  typename std::conditional_t<
    FuncArg_exists<Sig,0>,
    FunctionArgument<Sig, 0>,
    std::type_identity<Otherwise>
  >::type;
template<class T, class Otherwise>
struct TypeIfTuple {
  using type=Otherwise;
};
template<class...Ts, class Otherwise>
struct TypeIfTuple<std::tuple<Ts...>, Otherwise> {
  using type=std::tuple<Ts...>;
};
template<class T, class Otherwise>
using TypeIfTuple_t = typename TypeIfTuple<T,Otherwise>::type;

template<class Sig>
using TheTypeYouWant = TypeIfTuple_t<
  FirstArgIfExists<Sig, std::tuple<>>,
  std::tuple<>
>;