本文主要是介绍函数重载及SFINAE,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1. C语言不支持函数重载
main.c
// gcc main.c
void f(int x, int y){}void f(double x, double y){}int main(){}
使用指令gcc main.c
编译是会报错的,因为C语言是不支持函数重载的,报错信息如下:
main.c:4:6: error: conflicting types for ‘f’; have ‘void(double, double)’4 | void f(double x, double y){}| ^
main.c:2:6: note: previous definition of ‘f’ with type ‘void(int, int)’2 | void f(int x, int y){}| ^
换成g++ main.c
即可编译成功(或者将main.c
改成main.cpp
使用gcc main.cpp
也可编译成功,即让编译器把代码当作C++处理即可)。
2. 关于函数重载编译器会做的事
a. 将所有对应的“同名”候选函数,做成一个集合。
b.根据函数声明,从集合中移除一些不合适的候选函数。此步骤主要依靠SFINAE。
c.在集合中剩余的函数中,依据参数,挑选一个最合适的函数。如果挑选不到,或者无法决定哪个最合适,会报错。
d.如果步骤c中挑选到了最合适的函数,还会继续做一些检查,比如是否是delete
的。
3. SFINAE
3.1 SFINAE
简单示例
#include <iostream>
#include <typeinfo>
#include <type_traits>/* 此函数仅会被占用字节数大于4的类型匹配上 */
template<class T>
typename std::enable_if<(sizeof(T)>4)>::type len() {std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}/* 此函数仅会被占用字节数小于等于4的类型匹配上 */
template<class T>
typename std::enable_if<(sizeof(T) <= 4)>::type len() {std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}auto main()->int {len<double>(); // sizeof(double) is 8len<float>(); // sizeof(float) is 4len<char>(); // sizeof(char) is 1
}
3.2 不使用SFINAE
时编译器未匹配到合适的重载函数
#include <iostream>
#include <vector>
#include <array>
#include <typeinfo>template<class T, size_t N>
size_t foo(const T(&)[N]) {std::cout << "array's size: " << N << std::endl;return N;
}template<class T>
typename T::size_type foo(const T& container) {std::cout << "container's size: " << container.size() << std::endl; // 错误 C2039 "size": 不是 "Bar" 的成员return container.size(); // 错误 C2039 "size": 不是 "Bar" 的成员
}size_t foo(...) {std::cout << "variadic" << std::endl;return 0;
}class Bar {
public:using size_type = size_t;
};auto main()->int {int a[] = {1,2,3,4,5};std::vector<short> vec{ 5,6,7,8,9,0 };foo(a);foo(vec);foo(4);//std::cout << typeid(Bar::size_type).name() << std::endl;foo(Bar()); // !!!报错是因为此句!!!
}
对上述代码进行编译的时候会报错,报错信息如注释。
本想让foo(Bar());
匹配到size_t foo(...)
,但实际匹配到是template<class T> typename T::size_type foo(const T& container)
,而Bar
却没有size()
方法。
3.3 使用SFINAE
时让编译器匹配到合适的重载函数
SFINAE"Substitution Failure Is Not An Error"替换失败不是个错误。
#include <iostream>
#include <vector>
#include <array>
#include <typeinfo>template<class T, size_t N>
size_t foo(const T(&)[N]) {std::cout << "array's size: " << N << std::endl;return N;
}/* 通过T().size()的方式使得所有没有size()方法的类无法匹配该函数,从而达到SFINAE的目的 */
template<class T>
decltype(T().size(), typename T::size_type()) foo(const T& container) {std::cout << "container's size: " << container.size() << std::endl;return container.size();
}size_t foo(...) {std::cout << "variadic" << std::endl;return 0;
}class Bar {
public:using size_type = size_t;
};auto main()->int {int a[] = {1,2,3,4,5};std::vector<short> vec{ 5,6,7,8,9,0 };foo(a); // array's size: 5foo(vec); // container's size: 6foo(4); // variadicstd::cout << typeid(Bar::size_type).name() << std::endl; // mfoo(Bar()); // variadicdecltype(int()) ab;std::cout << double() << ", " << int() << std::endl; // 0, 0
}
3.4 利用std::enable_if
实现SFINAE
让编译器匹配到合适的重载函数
class Bar {
public:using size_type = size_t;
};template<class T, size_t N>
size_t foo(const T(&)[N]) {std::cout << "array's size: " << N << std::endl;return N;
}template<class T, class U = typename std::enable_if<!std::is_same_v<T, Bar>>::type>
typename T::size_type foo(const T& container) {std::cout << "container's size: " << container.size() << std::endl;return container.size();
}size_t foo(...) {std::cout << "variadic" << std::endl;return 0;
}auto main()->int {int a[] = {1,2,3,4,5};std::vector<short> vec{ 5,6,7,8,9,0 };foo(a);foo(vec);foo(4);//std::cout << typeid(Bar::size_type).name() << std::endl;foo(Bar());
}
输出:
array's size: 5
container's size: 6
variadic
variadic
4. std::enable_if
与std::is_same
用法拓展
4.1 示例一
#include <iostream>
#include <type_traits>class CFoo1{
public:using size_type = unsigned int;
};class CFoo2{
public:using size_type = unsigned int;
};template<class T, typename std::enable_if<std::is_same<T, CFoo1>::value>::type* = nullptr>
void bar(){std::cout << __PRETTY_FUNCTION__ << std::endl;
}template<class T, typename std::enable_if<std::is_same<T, CFoo2>::value>::type* = nullptr>
void bar(){std::cout << __PRETTY_FUNCTION__ << std::endl;
}auto main()->int {bar<CFoo1>();bar<CFoo2>();
}
输出:
void bar() [with T = CFoo1; typename std::enable_if<std::is_same<T, CFoo1>::value>::type* <anonymous> = 0]
void bar() [with T = CFoo2; typename std::enable_if<std::is_same<T, CFoo2>::value>::type* <anonymous> = 0]
4.2 示例二
与示例一的不同之处在于非类型的模板参数是否有默认值,示例一有默认值在调用时第二个参数可以不给,示例二无默认值调用时需要显示实例化。
#include <iostream>
#include <type_traits>class CFoo1{
public:using size_type = unsigned int;
};class CFoo2{
public:using size_type = unsigned int;
};template<class T, typename std::enable_if<std::is_same<T, CFoo1>::value>::type* /*= nullptr*/>
void bar(){std::cout << __PRETTY_FUNCTION__ << std::endl;
}template<class T, typename std::enable_if<std::is_same<T, CFoo2>::value>::type* /*= nullptr*/>
void bar(){std::cout << __PRETTY_FUNCTION__ << std::endl;
}auto main()->int {bar<CFoo1, nullptr>();bar<CFoo2, nullptr>();
}
输出:
void bar() [with T = CFoo1; typename std::enable_if<std::is_same<T, CFoo1>::value>::type* <anonymous> = 0]
void bar() [with T = CFoo2; typename std::enable_if<std::is_same<T, CFoo2>::value>::type* <anonymous> = 0]
4.3 示例三
#include <iostream>
#include <typeinfo>
#include <type_traits>template<class T>
typename std::enable_if<(sizeof(T)>4)>::type len() {std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}template<class T>
typename std::enable_if<(sizeof(T) <= 4)>::type len() {std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}auto main()->int {len<double>(); // sizeof(double) is 8len<float>(); // sizeof(float) is 4len<char>(); // sizeof(char) is 1
}
这篇关于函数重载及SFINAE的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!