推导 C++ 中类模板的构造函数参数类型?

问题描述 投票:0回答:1

我需要用 C++ 编写一个模板函数,提取类的构造函数参数的类型并将它们作为 std::tuple 返回。这就是我想要实现的目标:

#include <tuple>
#include <type_traits>

template <class T>
struct get_tuple
{
    // return the types of T's constructor arguments as a tuple
    // using type = ...
};

struct Test
{
    Test(int, double) {}
};

static_assert(std::is_same_v<typename get_tuple<Test>::type, std::tuple<int, double>>);

我尝试了以下代码,但失败了,因为我们无法获取指向构造函数的函数指针。

template <class T, class... Args>
std::tuple<Args...> get_tuple_impl(T (*)(Args...));

template <class T>
struct get_tuple
{
    using type = decltype(get_tuple_impl(&T::T));
};

我想要实现的是一个工厂,它根据配置中提供的参数创建 T 的实例。喜欢

template<class T>
T construct()
{
    typename get_tuple<T>::type args;
    // fill args with config
    return std::make_from_tuple<T>(args);
}
c++ templates tuples
1个回答
0
投票

但是,这种方法不适用于没有默认构造函数的类。 您面临的问题是由于构造函数是特殊成员函数,无法通过地址获取或直接用作函数指针。但是,您可以使用 SFINAE(替换失败不是错误)技术来推断构造函数的参数类型。

这是更新的实现:

#include <tuple>
#include <utility>

// Helper struct to extract constructor argument types
template <typename T, typename = void>
struct get_constructor_args;

// Specialization for non-aggregate initialization
template <typename T>
struct get_constructor_args<T, std::void_t<decltype(T{std::declval<std::add_pointer_t<T>>()})>>
{
    static constexpr auto size = 0;
    using type = std::tuple<>;
};

// Specialization for aggregate initialization
template <typename T>
struct get_constructor_args<T, std::enable_if_t<(sizeof...(std::tuple_element_t<0, T>) > 1)>>
{
    static constexpr auto size = sizeof...(std::tuple_element_t<0, T>);
    using type = std::tuple<std::remove_reference_t<decltype(std::get<0>(std::declval<T>()))>...>;
};

// Specialization for single-argument constructors
template <typename T, typename Arg>
struct get_constructor_args<T, std::void_t<decltype(T{std::declval<Arg>()})>>
{
    static constexpr auto size = 1;
    using type = std::tuple<Arg>;
};

// General case: try to find a matching constructor
template <typename T, typename... Args>
struct get_constructor_args<T, std::void_t<decltype(T{std::declval<Args>()...})>>
{
    static constexpr auto size = sizeof...(Args);
    using type = std::tuple<Args...>;
};

现在,您的

get_tuple
结构变得简单:

template <class T>
struct get_tuple : public get_constructor_args<T> {};

以下是定义

construct
函数的方法:

template <class T>
T construct() {
    typename get_tuple<T>::type args;
    // Fill args from configuration...
    return std::apply([](auto&&... args){ return T(args...); }, args);
}

此解决方案使用 SFINAE 来检测哪个专业化与给定类型匹配

T
。第一个专门化处理找不到构造函数的情况(例如,当
T
仅删除了构造函数时)。第二个专业领域涵盖聚合初始化。第三个处理单参数构造函数,而最后一个尝试匹配多参数构造函数。

请注意,此解决方案假设所有构造函数都有不同的参数列表;如果重载的构造函数具有不同数量的参数,则这将无法正常工作。另外,请记住,这不能很好地处理仅移动类型,因为它们在构造过程中需要对其参数进行左值引用。

使用示例:

int main() {
    struct MyType { MyType(int, int) {} };
    static_assert(std::is_same_v<typename get_tuple<MyType>::type, std::tuple<int, int>>);
    
    struct AnotherType { AnotherType(double) {} };
    static_assert(std::is_same_v<typename get_tuple<AnotherType>::type, std::tuple<double>>);
    
    struct AggregateType { int x; double y; };
    static_assert(std::is_same_v<typename get_tuple<AggregateType>::type, std::tuple<int, double>>);
    
    return 0;
}

© www.soinside.com 2019 - 2024. All rights reserved.