C++11:如何定义一个可以接受特定类型对象的通用引用的函数?

7
问题: 我正在使用C++11开发程序。 我想编写一个同时接受rvalue引用和lvalue引用的函数(即通用引用)的函数。
以下函数接受通用引用参数:
template<class T> void function(T&& t){/*SNIP*/}

然而,该函数接受所有类型的参数。这破坏了函数的类型安全性。如果我想让它接受特定类型的参数怎么办?

以下是我能想到的解决方案:

void function(Class& t){/*SNIP*/}
void function(Class&& t){ function(t); }

然而,这很丑陋。如果我想改变要接受的参数或更改函数名称,我必须更新函数的两个版本。有没有更好的等效方法?

编辑:问题已解决。你们两个都回答得很好。我投了+1票给两个答案以表达我的感激之情。我会把这个问题留下几天。获得最多赞同票的答案将被接受。

编辑2:最终我采用了以下代码:

template < class T,
class=typename std::enable_if<std::is_same<Class, typename std::decay<T>::type>::value>::type //Dummy template parameter
>
void function(T&&){}

编辑3:为此目的,我编写了一个宏定义:

#define uRefType(T, typeLimit) class T, class=typename std::enable_if<std::is_same<typename std::decay<T>::type, typeLimit>::value>::type

使用示例:

template< uRefType(T, Class) > void function(T&&){}

你提出的解决方案没有保留右值引用。你是不是想要 void function(Class&& t){/*COPY OF SNIP*/} - mucaho
2个回答

6

一种实现这个的方法是使用std::enable_if。这是由type_traits头文件提供的一个结构体。它被定义为当布尔条件A在编译时评估为true时,enable_if<A,B>::type是类型B。否则为空。

因此,如果你有一个函数模板

template <typename T>
void fun(T &&)
{ /*...*/ }

如果您希望确保只有在T是特定类型时才定义,可以使用enable_if<...>::type结构代替返回类型(这里是void)。布尔条件A的定义类似于: Tint,而类型B则被定义为函数的原始返回类型(这里是void)。
因此,如果我们希望只有当Tint时才定义fun,我们可以得到以下结果:
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_same<int,typename std::decay<T>::type>::value,void>::type
fun(T &&)
{ }

int main()
{
  int    lvali = 3;
  double lvald = 3.3;

  fun(3);
  fun(lvali);

  // fun(3.3);      // this won't be accepted (not an int)
  // fun(lvald)     // this won't be accepted (not an int)

  return 0;
}

注意到布尔条件如下所示(为了更好的可读性省略了std::):
is_same<int,typename decay<T>::type>::value
decay语句被用于确保无论Tint还是int &(以及其他一些特殊情况),这种技巧都能奏效。
进一步说明:只有在所考虑的函数的定义对rvalue和lvalue都相同的情况下,这种技巧才真正有用。 在许多情况下,这并不是这种情况(因为rvalue情况将实现移动,而lvalue则不会或类似的)。 当函数体非常短且仅将参数转发到另一个(可能是重载的)函数调用时,两个定义实际上相同的典型情况是:
template <typename T>
void fun(T &&obj)
{ other_fun(std::forward<T>(obj)); }

在这种情况下,可能不需要使用任何 enable_if 或其他技巧,因为 other_fun 的声明将确保最终只接受特定类型。

1
使用 std::enable_ifstd::is_same
template<class T, 
         class = typename std::enable_if<std::is_same<float, T>::value>::type>
void function(T&& t){/*SNIP*/}

并且(正如 jogojapan 的回答中所述,我忘记提到的)在 T 上使用 std::decay,因为 T& 不是与 T 相同类型,而 const T 也不同于 T

template<
  class T, 
  class = typename std::enable_if<
                       std::is_same<float,
                                    typename std::decay<T>::type>::value>::type>
void function(T&& t){/*SNIP*/}

http://ideone.com/ztkHsf 查看演示。

2
我在使用模板方面是个新手。你能给我展示一些示例代码吗? - Konfle Dolex
顺便说一下,我正在尝试通过阅读以下内容来解决问题:http://en.cppreference.com/w/cpp/types/enable_if http://en.cppreference.com/w/cpp/types/is_same。我会告诉你我是否能找到解决方案。 - Konfle Dolex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接