编译模板成员函数的时间扣除

时间:2015-03-10 22:55:58

标签: templates c++11 member-functions

是否可以在编译时获取模板成员函数的返回类型?

我想我需要的是:

template<class T>
struct SomeClass
{  
     // T must have a function foo(int), but do not know the
     // return type, it could be anything
     using RType = ??? T::foo(int) ???; // Is it possible to deduce it here?
}

2 个答案:

答案 0 :(得分:2)

使用decltype运算符和std::declval模板可以实现您想要做的事。

decltype(EXPRESSION)在编译时产生EXPRESSION所具有的类型。永远不会评估EXPRESSION本身。这很像sizeof(EXPRESSION)返回任何EXPRESSION求值的大小而没有实际评估它。

只有一个问题:您的foo是非static成员函数,因此编写decltype(T::foo(1))是一个错误。我们需要获得T实例。即使我们对它的构造函数一无所知,我们也可以使用std::declval来获取对它的实例的引用。这是纯粹的编译时间。 std::declval实际上从未被定义(仅被声明),因此不要尝试在运行时对其进行评估。

以下是它们的组合方式。

#include <type_traits>

template <typename SomeT>
struct Something
{
  using RetT = decltype(std::declval<SomeT>().foo(1));
};

要了解它确实有效,请考虑此示例。

struct Bar
{
  float
  foo(int);
};

struct Baz
{
  void
  foo(int);
};

int
main()
{
  static_assert(std::is_same<float, Something<Bar>::RetT>::value, "");
  static_assert(std::is_same<void, Something<Baz>::RetT>::value, "");
}

虽然这就是我认为你所要求的,但是如果你试图用Something<T>实例化T而不是拥有一个合适的foo成员,你会得到一个硬编译错误。最好将类型计算移动到模板参数中,以便您可以从SFINAE规则中受益。

template <typename SomeT,
          typename RetT = decltype(std::declval<SomeT>().foo(1))>
struct Something
{
   // Can use RetT here ...
};

答案 1 :(得分:0)

如果您知道函数调用的参数类型,则以下内容应该有效:

template<typename T>
struct X
{
  typedef typename decltype(std::declval<T>.foo(std::declval<int>())) type;
};

如果不这样做,您仍然可以推断出函数指针的类型并提取返回类型:

template<class F>
struct return_type;

template<class C, class R, class... Args>
struct return_type<R(C::*)(Args...)>
{ using type = R; };

template<typename T>
struct X
{
  typedef typename return_type<decltype(&T::foo)>::type type;
};

如果T::foo是重载函数或T成员,则会失败。

不幸的是,如果你知道你将要调用它的哪个参数(不幸的是,通常与你需要知道返回类型的位置不同),只能知道某个表达式的返回类型。