我需要用 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);
}
但是,这种方法不适用于没有默认构造函数的类。 您面临的问题是由于构造函数是特殊成员函数,无法通过地址获取或直接用作函数指针。但是,您可以使用 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;
}