C++ 使用模板元编程生成函数
C++ generate function with template metaprogramming
如何使用模板元编程生成函数。我想做的是有一堆功能基本上做同样的事情:
Type1 fun1(int arg1, int arg2) {
Type1 newType1 = {};
newType1.arg1 = arg1;
newType1.arg2 = arg2;
return newType1;
}
Type2 fun2(int arg1, int arg2, int arg3, bool arg4) {
Type2 newType2 = {};
newType2.arg1 = arg1;
newType2.arg2 = arg2;
newType2.arg3 = arg3;
newType2.arg4 = arg4;
return newType2;
}
所以基本上我不想自己编写所有这些函数,例如我想说我想要一个函数 fun1
接受两个 int 参数并使用模板将它们分配给 Type1 的新对象但是如何?
我的想法是有一个模板函数,它接受一个类型(这里是 Type1 或 Type2)和指向这些类型成员的指针,所以我唯一要做的就是为模板提供指向-成员并生成接受相应类型参数的函数。
这是一个c++17答案:
template<auto PMem>
struct member_type {};
template<class T, class M, M(T::*ptr)>
struct member_type<ptr> { using type=M; };
template<auto PMem>
using member_type_t=typename member_type<PMem>::type;
template<class T, auto...PMem>
T func( member_type_t<PMem>... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
}
测试代码:
struct Bob {
int x,y;
};
int main() {
Bob b = func<Bob, &Bob::x, &Bob::y>( 2, 3 );
(void)b;
}
不匹配类型也可以完美转发。这有它不起作用的缺点:
struct A {
int x, y;
};
struct B {
A one, two;
};
B func<B, &B::one, &B::two>( {1,2}, {3,4} );
但它确实消除了上面的一些样板文件,并且它可以消除每个成员字段的冗余移动。
为此,只需完全删除 member_type
助手:
template<class T, auto...PMem, class...Args>
T func( Args&&... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<Args>(args)), ... );
return retval;
}
在 c++17 之外做这件事很痛苦。您缺少 auto
参数和 ...
语句扩展。第二个相对容易使用一些样板来解决,但是第一个使您想要的语法基本上不可能;您可能会减少使用宏。
如果您不想要 <>
语法:
template<class T, auto...PMem>
constexpr auto make_func() {
return +[]( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
struct Bob {
int x,y;
};
constexpr auto* func = make_func<Bob, &Bob::x, &Bob::y>();
除了重载不可用外,constexpr 函数指针应该与函数几乎没有区别。
在 MSVC 中,您可能必须像这样消除函数指针类型的歧义:
template<class T, auto...PMem>
using func_t = T(*)(member_type_t<PMem>...);
template<class T, auto...PMem>
constexpr func_t<T, PMem...> make_func() {
return []( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
有时 MSVC 在具有多个不同调用约定选项的无状态 lambda 上存在一元运算符 +
问题。以上避免了这个问题,代价是一些样板文件。
如何使用模板元编程生成函数。我想做的是有一堆功能基本上做同样的事情:
Type1 fun1(int arg1, int arg2) {
Type1 newType1 = {};
newType1.arg1 = arg1;
newType1.arg2 = arg2;
return newType1;
}
Type2 fun2(int arg1, int arg2, int arg3, bool arg4) {
Type2 newType2 = {};
newType2.arg1 = arg1;
newType2.arg2 = arg2;
newType2.arg3 = arg3;
newType2.arg4 = arg4;
return newType2;
}
所以基本上我不想自己编写所有这些函数,例如我想说我想要一个函数 fun1
接受两个 int 参数并使用模板将它们分配给 Type1 的新对象但是如何?
我的想法是有一个模板函数,它接受一个类型(这里是 Type1 或 Type2)和指向这些类型成员的指针,所以我唯一要做的就是为模板提供指向-成员并生成接受相应类型参数的函数。
这是一个c++17答案:
template<auto PMem>
struct member_type {};
template<class T, class M, M(T::*ptr)>
struct member_type<ptr> { using type=M; };
template<auto PMem>
using member_type_t=typename member_type<PMem>::type;
template<class T, auto...PMem>
T func( member_type_t<PMem>... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
}
测试代码:
struct Bob {
int x,y;
};
int main() {
Bob b = func<Bob, &Bob::x, &Bob::y>( 2, 3 );
(void)b;
}
不匹配类型也可以完美转发。这有它不起作用的缺点:
struct A {
int x, y;
};
struct B {
A one, two;
};
B func<B, &B::one, &B::two>( {1,2}, {3,4} );
但它确实消除了上面的一些样板文件,并且它可以消除每个成员字段的冗余移动。
为此,只需完全删除 member_type
助手:
template<class T, auto...PMem, class...Args>
T func( Args&&... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<Args>(args)), ... );
return retval;
}
在 c++17 之外做这件事很痛苦。您缺少 auto
参数和 ...
语句扩展。第二个相对容易使用一些样板来解决,但是第一个使您想要的语法基本上不可能;您可能会减少使用宏。
如果您不想要 <>
语法:
template<class T, auto...PMem>
constexpr auto make_func() {
return +[]( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
struct Bob {
int x,y;
};
constexpr auto* func = make_func<Bob, &Bob::x, &Bob::y>();
除了重载不可用外,constexpr 函数指针应该与函数几乎没有区别。
在 MSVC 中,您可能必须像这样消除函数指针类型的歧义:
template<class T, auto...PMem>
using func_t = T(*)(member_type_t<PMem>...);
template<class T, auto...PMem>
constexpr func_t<T, PMem...> make_func() {
return []( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
有时 MSVC 在具有多个不同调用约定选项的无状态 lambda 上存在一元运算符 +
问题。以上避免了这个问题,代价是一些样板文件。