在C ++中定义协变模板函数的正确方法

时间:2015-11-17 06:47:11

标签: c++ templates

定义模板函数的最佳和最正确的方法是什么,其参数的类型必须是某个基类的子类?

这就是我所拥有的:

class A {};

class B : public A {};

class C {};

template <typename T>
int foo(T& x) // NB: need a solution that allows a return value
{
    A& dummy = x; // require T to be a subclass of A, else SFINAE
    return 1;
}

//int foo(C&) { return 2; }

int main()
{
   B b;
   C c;

   std::cout << foo(b) << "\n";
   std::cout << foo(c) << "\n"; // should fail unless foo(C&) is defined
}

以上是否正确?我不喜欢它,因为它没有表达意图。我更喜欢看起来更像下面的虚假代码:

template <class A T>
void foo(T& x) {}

也许enable_if可以某种方式使用?

(背景:在我的生产代码中,函数是运算符。我需要它们作为元编程原因的模板(推导出T的编译时类型)。但是我想限制它们只匹配A的子类。)< / p>

更新#1:Template Constraints C++的相关问题中,提供了以下内容:

static_assert(std::is_base_of<A, T>::value, "T must be a sublass of A");

这比A& dummy = x;更好地捕获意图但是它遇到了同样的问题,如果void foo(C&) { }被注释掉,foo<T>仍然专门用于调用foo(c)和然后static_assert失败了。鉴于参数不是A的子类,我希望编译器甚至不尝试专门化foo<T>

更新#2:根据此处接受的答案:Why does enable_if_t in template arguments complains about redefinitions?以下代码似乎已关闭,但它是特定于C ++ 14的。我想要一个可以移植到当前标准的解决方案。

template <typename T, std::enable_if_t<std::is_base_of<A, T>::value>* = nullptr>
void foo(T& x)
{
}

上述优点是,当定义void foo(C&)时,编译器会给出正确的错误:&#34;错误:没有用于调用&f; foo(C&amp;)&#39的匹配函数;&#34;

2 个答案:

答案 0 :(得分:2)

取决于你真正想要的支票。

要检查公共的,明确的基础,请在指针上使用is_convertible

template <class T>
auto foo(T& x) -> std::enable_if_t<std::is_convertible<T*, A*>{}/*, void*/> {
  // stuff
}

要检查任何基础,公共,受保护或隐私,含糊不清或明确,请使用is_base_of

template <class T>
auto foo(T& x) -> std::enable_if_t<std::is_base_of<A, T>{}/*, void*/> {
  // stuff
}

答案 1 :(得分:0)

以下是另一种方法,基于T.C.的回答,以及这里接受的答案:(Why does enable_if_t in template arguments complains about redefinitions?):

#include <type_traits>
#include <iostream>

// Provide enable_if_t in C++11
#if __cplusplus <= 201103L
namespace std {
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
}
#endif

class A {};

class B : public A {};

class C {};

template <typename T, std::enable_if_t<std::is_convertible<T*, A*>::value>* = nullptr>
int foo(T& x)
{
  return 1;
}

//int foo(C& x) { return 2; }

int main()
{
  B b;
  C c;

  std::cout << foo(b) << "\n";
  std::cout << foo(c) << "\n"; // should fail unless foo(C&) is defined
}

foo未定义时,这会正确忽略模板int foo(C& x),因此会为foo(c)行提供预期的(所需)编译器错误:

  

错误:没有匹配函数来调用'foo'