1. 只编译会用到的if
分支
示例代码一中,checkType_v1
和checkType_v2
两个函数的区别就是if
的条件里一个加了constexpr
一个没加,加与不加从结果来看都一样,那在编译时和运行时各有什么区别呢?
示例代码一,test_01.cpp
:
// g++ test_01.cpp -std=c++17
#include <iostream>
#include <type_traits>
template<class T>
void checkType_v1(T){
if constexpr (std::is_same_v<T, int>){
std::cout << "Input is an int.\n";
}
else if constexpr (std::is_same_v<T, float>){
std::cout << "Input is a float.\n";
}
else if constexpr (std::is_same_v<T, double>){
std::cout << "Input is a double.\n";
}
else{
std::cout << "Unsupported type!\n";
}
}
template<class T>
void checkType_v2(T){
if (std::is_same_v<T, int>){
std::cout << "Input is an int.\n";
}
else if (std::is_same_v<T, float>){
std::cout << "Input is a float.\n";
}
else if (std::is_same_v<T, double>){
std::cout << "Input is a double.\n";
}
else{
std::cout << "Unsupported type!\n";
}
}
int main(){
checkType_v1(4); // Input is an int.
checkType_v1(4.f); // Input is a float.
checkType_v1(4.0); // Input is a double.
checkType_v1('A'); // Unsupported type!
checkType_v2(4); // Input is an int.
checkType_v2(4.f); // Input is a float.
checkType_v2(4.0); // Input is a double.
checkType_v2('A'); // Unsupported type!
}
【来自C++大咖吴老师的解答】如果你要调用一个只接受某种类型的函数,那就必须用 if constexpr
。此外,用 if constexpr
条件判断是编译是做出的,没用到的分支完全不会在某个类型的特化里产生二进制代码。
举例说明,也就是说针对checkType_v1
的版本,假设调用它的时候传入的是int
类型的参数,那么编译的二进制文件中只有代码里的第一个分支的实现。而checkType_v2
的版本的二进制文件中是有整个函数的实现。
这么做的目的有如下三个(其中第三个最重要):
- 降低运行时的判断时间;
- 减少编译后二进制文件的大小;
- 【来自C++大咖吴老师的解答】但最重要的是,有些情况下你对特定类型要走的分支在其他类型的情况下可能完全编译不通过!比如,
vector
可以reserve
,deque
不可以;deque
可以push_front
,vector
不可以。
2. 验证上面第3点
示例代码二,test_02.cpp
:
// g++ test_02.cpp -std=c++17
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <vector>
#include <set>
#include <deque>
template<class Container>
void expandContainer(Container& container, int val){
if constexpr (std::is_same_v<Container, std::vector<int>>){
container.push_back(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "vector number added.\n";
}
else if constexpr (std::is_same_v<Container, std::deque<int>>){
container.push_front(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "deque number added.\n";
}
else{
container.insert(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "Other container number added.\n";
}
}
int main(){
std::vector<int> vec{1,2,3};
expandContainer(vec,100); // 1, 2, 3, 100, vector number added.
std::deque<int> deq{4,5,6};
expandContainer(deq, 200); // 200, 4, 5, 6, deque number added.
std::set<int> aset{7,8,9};
expandContainer(aset, 300); // 7, 8, 9, 300, Other container number added.
假如去掉constexpr
,如示例代码三,test_03.cpp
:
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <vector>
#include <set>
#include <deque>
template<class Container>
void expandContainer(Container& container, int val){
if constexpr (std::is_same_v<Container, std::vector<int>>){
container.push_back(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "vector number added.\n";
}
else if /*constexpr*/ (std::is_same_v<Container, std::deque<int>>){
container.push_front(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "deque number added.\n";
}
else{
container.insert(val);
for(const auto&it: container){
std::cout << it << ", ";
}
std::cout << "Other container number added.\n";
}
}
int main(){
std::vector<int> vec{1,2,3};
expandContainer(vec,100); // 1, 2, 3, 100, vector number added.
//std::deque<int> deq{4,5,6};
//expandContainer(deq, 200); // 200, 4, 5, 6, deque number added.
std::set<int> aset{7,8,9};
expandContainer(aset, 300); // 7, 8, 9, 300, Other container number added.
}
此时编译会有如下报错:
走哪个分支如果在编译时无法确定那么就要保障运行时所有的分支都可以走,很显然else if
的分支中有push_front
操作,但std::set
不支持,所以编译会报错。
因此,如果传入的参数只能走特定的分支,只能在编译时就限制住走的路径,即使用if constexpr
、else if constexpr
。