C ++模板参数类型推导

时间:2017-02-08 22:53:29

标签: c++ c++11 templates

给出一个模板化的函数,声明如下:

template<class T>
int Function(T object);

用户可以通过指定模板化类型来调用此函数,如下所示:

int result = Function<float>(100.f); // Valid

但是类型规范是可选的,因为编译器可以从提供的参数的类型中推导出T的类型;像这样:

int result = Function(100.f); // Also valid, the compiler deduced the type "float" from the literal's type

假设我有点复杂,我想要一个这样的模板化值参数:

template<class T, T* object>
int Function();

我可以这样调用我的函数:

static float val = 100.f;
// ...
int result = Function<float, &val>();

我的问题是:我是否有任何方法强制编译器根据参数类型推断类型T&amp; val?

我需要的是使以下代码有效的方法:

static float val = 100.f;
// ...
int result = Function<&val>();

可以吗?

2 个答案:

答案 0 :(得分:7)

在C ++ 17中,您可以拥有auto非类型模板参数。这将让您解决问题。

喜欢

template<auto object, class T=std::decay_t<decltype(*object)>>
int Function();

(假设您希望T的正文中的Function类型

在C ++ 14中,缺少C ++ 17功能。它的添加完全是因为它丢失了。变通办法涉及像#define UGLY_HACK(...) decltype(__VA_ARGS__), __VA_ARGS__这样的宏。

答案 1 :(得分:5)

注意:这里的答案借鉴了有效的现代C ++,并且添加了很少(

这是容易摆出但难以回答的问题之一!我记得读了整整一章。关于模板类型演绎和新手读者的答案,答案也不清楚。不过我会在这里澄清一下。

应该注意,有一些叫 通用引用 (与引用或r值引用不同)影响模板类型推导,我假设读者了解l值和r值参考。

任何普遍存在的函数模板定义如下所示:

template <typename T>
returnType function(paramType param);

对函数的调用会看起来像这样:

function(expression);

编译器使用表达式来确定 T 的类型和 paramType 的类型。之所以如此,是因为 paramType 更常包含 const const&amp; const&amp;&amp; 等装饰。初学者会想要相信编译器推导出的 T 类型与表达式的类型相同,即传递给函数的参数,但并不总是如此案子。类型 T 的扣除取决于表达式 paramType 。根据函数参数 paramType 的不同,有三种情况需要考虑用于模板类型扣除:

  1. paramType 是指针或引用,但不是通用引用
  2. paramType 是一种通用参考
  3. paramType 既不是指针也不是引用。
  4. 让我们逐一看看每个案例

    案例1:paramType是指针或引用,但不是通用引用

    叫我疯了,但这是最简单的案例。在这种情况下,类型推导的工作方式如下: (i)如果 表达式 是引用,则忽略引用部分 (ii)然后将 表达式 模式与 paramType 进行匹配,以确定 T

    让我们来看一个例子:

    template <typename T>
    returnType function(T &param);
    

    我们有以下变量声明:

    int x = 23;               // x is int
    const int const_x = x;    // const_x is const int
    const int& ref_x = x;     // ref_x is a reference to x as const int
    

    各种调用中对 T param 的推断呼叫如下:

    f(x);                    //T is int, param's type is int&
    f(const_x);              //T is const int, param's type is const int&
    f(ref_x);                //T is const int, param's type is const int&
    

    这里有两点需要注意:

    (i)编译器在此处忽略引用ness

    (ii)当将const对象或引用传递给const对象时,const-ness成为 T 类型的一部分,因此传递const对象或引用使用参数 T&amp; 的函数的const对象是安全的。

    如果我们将函数参数从 T&amp; 更改为 const T&amp; ,因为在这种情况下我们假设 param 引用 const const -ness不需要被推断为 T 的一部分。以下是一个例子:

    template <typename T>
    returnType function(const T& param);  // param is now a ref-to-const
    
    int x = 23;                    // same as previous
    const int const_x = x;         // same as previous
    const int& ref_x = x;          // same as previous
    
    f(x);                         // T is int, paramType is const int&
    f(const_x);                   // T is int, paramType is const int&
    f(ref_x);                     // T is int, paramType is const int&
    

    注意:变量&#39; x&#39;不是&#39; f()&#39;的const参数。但它被推断为const param

    如果 paramType 是一个指针,那么事情的基本工作方式与引用相同,只是会有指针而不是引用。例如,为了完整起见,提供了以下内容:

    template <typename T>
    returnType function( T* param);  //paramType is now a pointer
    
    int x = 23;                      // same as before
    const int *pointer_x = &x;       // pointer_x is pointer to x as const int
    
    f(&x);                          // T is int, paramType is int*
    f(&pointer_x);                  // T is const int, paramType is const int*
    

    为了完整起见,如果 paramType 是指向常量对象的指针,我也可以发布案例,如下所示:

    template <typename T>
    returnType function(const T* param);
    
    int x = 23;                      // same as before
    const int *pointer_x = &x;       // pointer_x is pointer to x as const int
    
    f(&x);                          // T is int, paramType is const int*
    f(&pointer_x);                  // T is int, paramType is const int*
    

    ,再次 const -ness不再被推断为T

    的一部分

    如果是r值引用,则输入 T paramType 扣除遵循基本相同的规则在l值引用的情况下。

    这涵盖了第一种情况的大部分内容。让我们来看看我们的案例2.

    案例2:paramType是一个通用引用

    通用引用被声明为r值引用但是采用l值但是它们的行为不同的是函数参数接收l值引用。以下是类型推导如何适用于这种情况:

    (i)如果 表达 是l值, T paramType 被推断为l值。 (面对代码的样子,这看起来很奇怪,因为虽然使用r值引用的语法声明了 paramType ,但它的推导类型是l值引用。)应该注意的是,这是 T 被推断为参考的唯一情况。

    以下示例澄清了我的解释:

    template <typename T>
    returnType function(T&& param);  //param becomes universal reference if
                                    // argument to function call is an l-value
    
    int x = 23                     // same as previous
    const int const_x = x;         // same as previous
    const int& ref_x = x;          // same as previous
    
    f(x);             // x is l-value therefore T is int&
                      // paramType is int&
    
    f(const_x);       // const_x is l-value therefore T is const int&
                      //paramType is also const int&
    
    f(ref_x);        // ref_x is l-value therefore T is const int&
                     //paramType is also const int&
    
    f(23);          // 27 is r-value so T is int
                    // paramType is now int&&
    

    我想在这里说实话并说这并不能解释为什么通用引用会像他们那样工作,但我认为如果我继续在这里证明这一点,这篇文章会变得太冗长。

    案例3:paramType既不是指针也不是引用##

    这是模板中的值传递的地方,这意味着param将是传递给调用函数的参数的任何内容的副本,即完全是一个新对象,这会激发控制类型推导的规则来自 表达 T 这里要注意的两点是:

    (i)忽略 表达式 中的 refrence - ,如果恰好有一个。

    (ii)在忽略 ref -ness之后,忽略 const -ness或 volatile -ness,即如果存在

    template <typename T>
    returnType function(T param);
    
    int x = 23;
    const int const_x = x;
    const int& ref_x = x;
    
    f(x);             // T and paramType are both int
    f(const_x);       // T and paramType are both int here too
    f(ref_x);         // T and paramType are both int again
    

    注意即使const_x和ref_x是不能修改的const对象,也不意味着它们的副本无法修改。 这看起来很简单,但是当我们将常量指针传递给常量对象时它会变得棘手。让我们看看另一个例子:

    template <typename T>
    returnType function(T param);
    
    const double *const dPtr = 23;  //dPtr is const pointer to const double
    
    function(dPtr);             //passing argument of type const double *const
    

    const 指针按值传递时, const -ness将丢失,指针将按值复制,这​​与传递的类型推导规则同步按值,但指针指向的 const - 的值被保留,因此 paramType 将是const * double。

    当我开始了解它时,这可能让你头脑旋转。最好的方法是重新阅读并尝试编写代码。