如何使用元编程过滤const类型和非const类型?

时间:2014-08-28 17:43:56

标签: c++ c++11 template-meta-programming type-deduction

我有这段代码

#include <iostream>

size_t F()
{
        return 0;
}

template <class Type, class... NextTypes>
size_t F(const Type& type, const NextTypes&... nextTypes)
{
        if (!std::is_const<Type>::value)
                return sizeof(type) + F(nextTypes...);
        else
                return F(nextTypes...);
}

int main()
{
  int a = 0;
  const int b = 0;
  const size_t size = F(a,b);
  std::cout << "size = " << size << std::endl;
  return 0;
}

我试图在编译时知道常量参数和非常量参数的总大小。当前输出为8,由于某种原因,编译器认为b不是常数,我使用typeiddecltype来打印a和{{1}的类型确实输出显示bb而不是int,正如我预期的那样。我错过了什么?是否可以将可变参数集合分离为const参数和非const?

1 个答案:

答案 0 :(得分:6)

考虑这个功能模板:

template<typename T>
void deduce(const T&);

如果让编译器从参数表达式推导出T的类型,推导出的类型将永远不会是const:它会尝试使函数参数的const T相同到用于调用函数的参数表达式的类型。例如:

struct cls {};
const cls c;

deduce(c) // deduces T == cls

通过推导T == cls,编译器成功地使const T与参数类型const cls相同。编译器没有理由为const和非const参数类型生成两个不同的函数;在任何情况下,函数模板实例化的参数类型都将是const限定的:您通过说const T&而不是T&来请求它。


您可以通过 cv-限定函数参数来推断出参数的常量:

template<typename T>
void deduce(T&);

但是,这将无法绑定到非const临时值(rvalues)。为了支持它们,您可以使用通用引用

template<typename T>
void deduce(T&&);

如果参数是左值,这将推导出T的左值引用类型,如果参数是右值,则不会引用参考类型。这个常数将被正确推导出来。

例如,如果参数的类型为const A并且是左值,则T将推断为const A&。然后,函数参数为const A& &&,折叠为const A&(左值参考)。如果参数是右值,则T将推导为const A,函数参数将变为const A&&(右值参考)。

请注意,由于T在这种情况下可以作为引用,因此您需要在检查const-ness之前将其删除:std::is_const< typename std::remove_reference<T>::type >::value