函数模板(参考《C++ Templates 英文版第二版》)

函数编程(参考《C++ Templates 英文版第二版》)

Chapter 1 函数模板

1.1 一窥函数模板

template<class T>
T max(T a, T b)
{return b < a ? a : b;
}
#include "max1.hpp"
#include <iostream>
#include <string>
#include <format>int main(int argc, char* argv[])
{int i = 42;std::cout << std::format("max(7,i):{}\n", ::max(7, i)); // C++20double f1 = 3.4;double f2 = -9.3;std::cout << std::format("max(f1,f2):{}\n", ::max(f1, f2));std::string s1 = "i am very intelligent";std::string s2 = "i am intelligent";/** 如果不使用::限定max为全局* 则默认使用std::max(),对于在std空间内的类型*/std::cout << std::format("max(s1,s2):{}\n", ::max(s1, s2)); }

输出:

max(7,i):42
max(f1,f2):3.4
max(s1,s2):i am very intelligent

模板不会被编译成一个可以处理任何类型的实例,而是不同的类型使用模板时,模板会生成不同的实例

通过确定的参数代替模板参数的过程叫实例化

1.1.3 两阶段Translation

为了一个不支持相应操作的类型实例化模板会导致错误

例如,复数比较大小

因此,模板的"编译"分两步:

  1. 在没有实例化前:
    • 检查语法错误
    • 使用不依赖模板参数的名字
    • 检查不依赖于模板参数的静态断言
  2. 在实例化阶段,模板代码被检查是否有效,所有依赖于模板的代码都将被检查
template <typename T>
void f(T x) {undeclared();   // first-phase compile-time error if undeclared() unknownundeclared(x);  // second-phase compile-time error if undeclared(T) unknownstatic_assert(sizeof(int) > 10,"int too small");  // always fails if sizeof(int) <= 10static_assert(sizeof(T) > 10,"T too small");  // fails if instantiated for T with size <= 10
}

有些编译器不进行完全的第一阶段检查,你有可能观察不到上述现象

1.2 函数参数推断(Template Argument Deduction)

当我们调用函数模板时,模板参数将由我们传入的参数确定

但是,T 可能只是参数的"一部分",例如,我们我们使用常量引用声明max()


template<typename T>
T amx(T const& a,T const& b)
{return a < b ? b : a;
}

在类型推断中的类型转换

在类型推断中类型转换是有限的

  • 当使用引用传参时,参数类型必须完全匹配,编译器不会进行任何微小的转换
  • 当使用值传参时,支持退化的转换,const voaltile限定符会被忽略,引用转换为引用类型,数组,函数将会被转换成指针类型,两个参数退化(std::decayed)1后的类型必须匹配
template <typename T>
T max(T a, T b) {return b < a ? a : b;
}int a = 1;
const int b = 42;
int& c = a;
int arr[4];
std::string s;::max(a, b);        // OK:T 推断为 int
::max(b, b);        // OK:T 推断为 int
::max(a, c);        // OK:T 推断为 int
::max(&a, arr);     // OK:T 推断为 int*
::max(1, 3.14);     // 错误:T 推断为 int 和 double
::max("hello", s);  // 错误:T 推断为 const char[6] 和 std::string

有三个方法解决这个问题:

  1. 强制转换参数使他们都匹配

    std::cout << std::format("max1(1,4.4):{}", ::max(static_cast<double>(1), 4.4));
    
  2. 显示指定参数类型

    std::cout << std::format("max1(1,4.4):{}", ::max<double>(1, 4.4));
    

默认参数的类型推断

template<typename T>
void f(T = "")
{
}
f(1);  // OK:T 推断为 int,调用 f<int>(1)
f();   // 错误:不能推断 T

如果想要支持这个例子,你也可以声明一个默认参数类型

f();  // OK

1.3 多种参数模板

函数模板有两种不同的参数

  • template parameters 模板参数 , 尖括号里面的就是模板参数

  • call parameters 调用参数,就是参数列表里面的

    template<typename T> // 模板参数
    T max(T a,T b)       // a,b就是调用参数
    

    你可以使用多个模板参数

    
    template<typename T1,typename T2>
    T1 max(T1 a,T2 b)
    {return a < b ? b : a;
    }

    这个例子有一些问题,虽然他很有可能正常工作,但是他可能违背程序员的意愿,将返回值强制转换成T1,例如 double->int

    有三种方式可以避免这个问题:

    • 再加一个模板参数作为返回值类型
    • 让编译器清楚返回值类型
    • 把返回值声明成两个模板参数的共同类型

    我们接下来讨论:

    1.3.1 模板参数作为返回值

    我们可以明确的指定参数类型

    template<typename T>
    T max(T a, T b)
    {return a < b ? b : a;
    }
    ::max<int>(1,2.2);
    

    但是很多情况下,参数与返回值类型并没有关系,我们就可以声明第三个模板参数作为返回值

    template<typename T1, typename T2,typename  RT>
    RT max(T1 a, T2 b)
    {return a < b ? b : a;
    }
    

    但是模板参数推断并不会考虑到返回值,所也这么做是不行的,除非你手动指定返回值类型

    ::max<int,double,double>(1,3.3)
    

    显然,这样做并没有什么优势

    1.3.2 推断返回值

    
    template<typename T1,typename T2>
    auto max(T1 a,T2 b)
    {return a < b ? b : a;
    }
    

    auto使得真正返回值必须从函数体中推断出来

    然而,在一些情况下,函数传入参数是引用类型,你想返回值类型,这时你就需要使用std::decay1去掉修饰符

    template<typename T1, typename T2>
    auto max(T1 a, T2 b) ->typename std::decay<decltype(true?a:b)>::type
    {return a < b ? b : a;
    }
    

    1.3.3 返回公共类型(Common Type)

    上面的例子可以直接这么写

    #include <type_traits>template<typename T1, typename T2>
    std::common_type_t<T1,T2> max(T1 a, T2 b) 
    {return a < b ? b : a;
    }

1.4 默认模板参数

你也可以定义默认的模板参数

  1. 我们可以使用之前的模板参数

    template<typename T1, typename T2,typename RT = std::decay_t<decltype(true?T1():T2())>>
    RT max(T1 a, T2 b)
    {return a < b ? b : a;
    }
    

    std::decay_t确保不会返回引用

  2. 我们可以使用std::common_type_t<>std::decay相同的效果

    template<typename T1, typename T2, typename RT =std::common_type_t<T1,T2>>
    RT max(T1 a, T2 b)
    {return a < b ? b : a;
    }
    

1.5 函数模板重载

// maximum of two int values:
int max (int a, int b)
{return  b < a ? a : b;
}// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{return  b < a ? a : b;
}int main()
{::max(7, 42);          // calls the nontemplate for two ints::max(7.0, 42.0);      // calls max<double> (by argument deduction)::max('a', 'b');       // calls max<char> (by argument deduction)::max<>(7, 42);        // calls max<int> (by argument deduction)::max<double>(7, 42);  // calls max<double> (no argument deduction)::max('a', 42.7);      // calls the nontemplate for two ints
}

总之,确保只有一个函数匹配

#include <cstring>
#include <string>// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{return  b < a ? a : b;
}// maximum of two pointers:
template<typename T>
T* max (T* a, T* b)
{return  *b < *a  ? a : b;
}// maximum of two C-strings:
char const* max (char const* a, char const* b)
{return  std::strcmp(b,a) < 0  ? a : b;
}int main ()
{int a = 7;int b = 42;auto m1 = ::max(a,b);     // max() for two values of type intstd::string s1 = "hey";std::string s2 = "you";auto m2 = ::max(s1,s2);   // max() for two values of type std::stringint* p1 = &b;int* p2 = &a;auto m3 = ::max(p1,p2);   // max() for two pointerschar const* x = "hello";char const* y = "world";auto m4 = ::max(x,y);     // max() for two C-strings
}

如果用传引用实现模板,再用传值重载 C 字符串版本,不能用三个实参版本计算三个 C 字符串的最大值

#include <cstring>// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{return  b < a ? a : b;
}// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{return  std::strcmp(b,a) < 0  ? a : b;
}// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{return max (max(a,b), c);       // error if max(a,b) uses call-by-value
}int main ()
{auto m1 = ::max(7, 42, 68);     // OKchar const* s1 = "frederic";char const* s2 = "anica";char const* s3 = "lucas";auto m2 = ::max(s1, s2, s3);    // run-time ERROR
}

错误原因是 max(max(a, b), c) 中,max(a, b) 产生了一个临时对象的引用,这个引用在计算完就马上失效了

image-20210918182940269

重载版本必须在函数调用前声明才可见

第一章结束

未完待续

cpp/types/add_pointer)::type.Otherwise, the member typedef type is std::remove_cv<std::remove_reference::type>::type.These conversions model the type conversion applied to all function arguments when passed by value.The behavior of a program that adds specializations for decay is undefined.


  1. Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type. Formally:If T names the type “array of U” or “reference to array of U”, the member typedef type is U*.Otherwise, if T is a function type F or a reference thereto, the member typedef type is std::add_pointer::type.Otherwise, the member typedef type is std::remove_cv<std::remove_reference::type>::type.These conversions model the type conversion applied to all function arguments when passed by value.The behavior of a program that adds specializations for decay is undefined. ↩︎ ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/407961.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android FTP Server 1

下面介绍几种Android 版本的FTP Server : virtualdataline Virtual Data Line is a software that you can manage files of the phone on you pc without data line . Phone files can be copied to a computer and computer files can copied to the phone. With this …

Switcher ---Vista Areo 工具

推荐一个Vista Areo主题工具Switcher.可以从这里 [url]http://insentient.net/[/url] 获取 自由、创新、研究、探索…… 转载于:https://blog.51cto.com/shanyou/74271

很好的测试智商看看你能回答出来多少?一共75道!!

【1】假设有一个池塘&#xff0c;里面有无穷多的水。现有2个空水壶&#xff0c;容积分别为5升和6升。问题是如何只用这2个水壶从池塘里取得3升的水。 【2】周雯的妈妈是豫林水泥厂的化验员。 一天&#xff0c;周雯来到化验室做作业。做完后想出去玩。 "等等&#xff0c;妈…

react(84)--多张图片

<Form.Item label"上传封面图片"><BaseUploadImageonRef{(ref) > {this.upload ref;}}value{coverPaths}multiple/></Form.Item>

【ES实战】Elasticsearch6开始的CCR

【ES实战】学习使用Elasticsearch6开始的CCR 本文涉及官网文章地址 OverviewRequirements for leader indicesAutomatically following indicesGetting started with cross-cluster replicationUpgrading clusters CCR > Cross-cluster replication 文章目录 【ES实战】学…

可变参数模板(参考《C++ Templates 英文版第二版》)

可变参数模板(参考《C Templates 英文版第二版》) Chapter 4 可变参数模板 自从C11,模板可以接受可变数量的参数 4.1 可变参数模板 可以定义模板,去接受无限数量的模板参数 这种行为的模板叫做可变参数模板 4.1.1 例子 #include <iostream>template<typename T…

Curiously Recurring Template Pattern奇怪的模板递归 --- C++20

Curiously Recurring Template Pattern 奇怪的模板递归 — C20 我们都知道C有静态多态和动态多态,动态多态通过虚函数表实现,他的缺点就是对效率产生一点点影响 可以用CRTP解决这个问题 我们先举一个动态多态的例子: #include <iostream> using namespace std;class …

关于table的中元素对齐方式的注意点

情形一&#xff1a;<td></td>中嵌套了div或table元素。 1。td中的内部元素(如div)设置了height属性&#xff0c;td中设置了vertical-align的情况下&#xff0c;在IE中&#xff0c;td的vertical-align不起作用&#xff0c;但在firefox中是起作用的。 2。如果td中未…

不要带我去看鱼了

我在企业里面上班。每天中午都得回家吃午饭。儿子才3岁。每每见我总是要我抱&#xff0c;我经常出差什么的&#xff0c;儿子都怕我走了。每次我要离开的时候&#xff0c;只好让老婆抱着儿子遮住&#xff0c;让我悄悄地离开。儿子便猛哭。老婆只好带着儿子到家门口的池塘旁边去看…

char 与 String 相等比较

这是一个相当2 相当基础 相当没有意义的帖子&#xff1b;但今天因为这个问题引发了一个bug。小细节也很重要&#xff01;&#xff01;&#xff01; char a1;// char b2dsf; //char 表示单个字符 char c1; //不需要单引号也能表示// System.out.println(a.equals(&q…

我眼中的金蝶ERP

在我的博克回复里&#xff0c;有个小雨的博友问我是否了解金蝶ERP&#xff0c;我答应给她说说金蝶ERP&#xff0c;所以就有了这份文章。在我们国内&#xff0c;最出名的或者说市场占有率前两位的就是用友和金蝶了。至于到底谁排名先排名后一点&#xff0c;这不是我想说的范围。…

什么是JDO

From: http://blog.csdn.net/wp_84/archive/2007/09/02/1769481.aspx Java数据对象(Java Data Objects,JDO)是一个应用程序接口(API),它是Java程序员能够间接地访问数据库,也就是说,不需使用直接的结构化查询语言(SQL)语句.JDO是作为Java数据库连接(JDBC)的一个补充来介绍的,而…

PROJECT #0 - C++ PRIMER [CMU 15-445645]笔记

PROJECT #0 - C PRIMER [CMU 15-445/645]笔记 这是数据库领域的一门课程, 由卡内基梅隆大学副教授Andy Pavlo授课, 目前在网上有授课视频资料、实验以及配套的在线测评环境 (限时开放至2021年12月31日) 环境: wsl2 Clion Project #0 - C Primer 还是很简单的,主要目的是让…

简单JS实现对表的行的增删

这段代码非常的简单&#xff0c;仅仅作为自己的一个小小的记录&#xff01; ok&#xff0c;先上一个简单的图例&#xff0c;效果如下&#xff08;注意&#xff1a;这只是一个简单的例子&#xff0c;不过可以根据这个简单的例子&#xff0c;变化出更为复杂的效果&#xff09;&am…

用 JavaScript 操作字符串

虽然 JavaScript 有很多用处&#xff0c;但是处理字符串是其中最流行的一个。下面让我们深入地分析一下使用 JavaScript 操作字符串。在 JavaScript 中&#xff0c; String 是对象。 String 对象并不是以字符数组的方式存储的&#xff0c;所以我们必须使用内建函数来操纵它们的…

The Ranges Library --- C++20

The Ranges Library — C20 ranges可以让我们更加舒服的写代码了, 不用再敲那么多的代码 之前我们需要这样标准库的算法对容器的操作 #include <iostream> #include <ranges> #include <vector> #include <algorithm>int main() {std::vector vec{ …