提问者:小点点

用模板元编程C++生成函数


如何使用模板元编程生成函数。 我想做的是有一堆函数,它们基本上做同样的事情:

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)和指向这些类型的成员的指针,所以我要做的唯一一件事就是给模板指向成员的指针,然后它生成一个接受相应类型参数的函数。


共1个答案

匿名用户

这是一个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上遇到一元运算符+的问题。 上面的方法避免了这个问题,但代价是一些样板。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(模板|元|编程|c++|生成|函数)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?