基于类型属性的重载
算法重载
在一个泛型算法中引入更为特化的变体,这种设计和优化方式称为算法特化。之所以需要算法特化,原因有二:
- 针对特定类型使用更加合理的实现,对于const char *,less的第二个实现更加合理
template <typename T>
bool less(const T a, const T b) {return a < b;
}template <>
bool less(const char *a, const char *b) {return strcmp(a, b) < 0;
}
- 针对特定类型使用执行效率更高的实现,对于vector<int>,swap的第二个实现更加合理
template <typename T>
void swap(T &a, T &b) {T temp(a);a = b;b = temp;
}template <typename T>
void swap(std::vector<T> &a, std::vector<T> &b) {a.swap(b);
}
基于函数模板的部分排序规则,上述两个例子,都是第二种函数模板为更加特化的模板。存在更为特化的函数模板时,编译器会优先选择这类函数模板,只有其不适用时,编译器才会回退到更为泛化的版本。
但是,并非所有概念上更为特化的算法变体,都可以直接转换成提供了正确部分排序行为的函数模板,例如下面的例子:
#include <iostream>
#include <iterator>
#include <vector>template <typename random_access_iter, typename distance_type>
void advance_iter(random_access_iter &iter, distance_type distance) {iter += distance;
}template <typename input_iter, typename distance_type>
void advance_iter(input_iter &iter, distance_type distance) {while (distance > 0) {++iter;--distance;}
}int main(int argc, char **argv) {std::vector<int> vec {0, 1, 2, 3, 4, 5, 6};auto iter = vec.begin();advance_iter(iter, 2);std::cout << *iter << std::endl;return 0;
}
编译其无法通过模板参数名称来区分不同的模板函数,所以上面的代码无法通过编译,报错如下:
iter_specialazition.cpp:11:6: error: redefinition of 'advance_iter'
void advance_iter(input_iter &iter, distance_type distance) {^
iter_specialazition.cpp:6:6: note: previous definition is here
void advance_iter(random_access_iter &iter, distance_type distance) {^
iter_specialazition.cpp:21:5: error: no matching function for call to 'advance_iter'advance_iter(iter, 2);^~~~~~~~~~~~
iter_specialazition.cpp:11:6: note: candidate template ignored: substitution failure [with input_iter = std::__wrap_iter<int *>, distance_type = int]
void advance_iter(input_iter &iter, distance_type distance) {
因此,我们需要用到其他技术,更好的实现算法特化。
标签派发
标签派发并非一种C++语法特性,而是基于萃取的一种设计模式。具体方式:用一个唯一的,可以区分特定变体的类型,来标记不同算法的实现。可以使用该种设计模式,解决上节遇到的问题,源码如下:
#include <iostream>
#include <iterator>
#include <vector>template <typename iterator_type, typename distance_type>
void advance_iter_impl(iterator_type &iter, distance_type distance, std::random_access_iterator_tag) {std::cout << "std::random_access_iterator_tag" << std::endl;iter += distance;
}template <typename iterator_type, typename distance_type>
void advance_iter_impl(iterator_type &iter, distance_type distance, std::input_iterator_tag) {std::cout << "std::input_iterator_tag" << std::endl;while (distance > 0) {++iter;--distance;}
}template <typename iterator_type, typename distance_type>
void advance_iter(iterator_type &iter, distance_type distance) {advance_iter_impl(iter, distance, typename std::iterator_traits<iterator_type>::iterator_category());
}int main(int argc, char **argv) {std::vector<int> vec {0, 1, 2, 3, 4, 5, 6};auto iter = vec.begin();advance_iter(iter, 2);std::cout << *iter << std::endl;return 0;
}
Enable/Disable 函数模板
提供多种特化版本
算法特化需要 可以 基于模板参数的属性 进行选择的,不同的函数模板。从c++11开始提供的std::enable_if恰能接此重任。前面提到的问题,还可以通过enable_if实现,如下:
#include <iostream>
#include <iterator>
#include <vector>template <typename iterator_type>
constexpr bool is_random_access_iterator = std::is_convertible<typename std::iterator_traits<iterator_type>::iterator_category, std::random_access_iterator_tag>::value;template <typename iterator_type, typename distance_type>
typename std::enable_if<is_random_access_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::random_access_iterator_tag" << std::endl;iter += distance;
}template <typename iterator_type, typename distance_type>
typename std::enable_if<!is_random_access_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::input_iterator_tag" << std::endl;while (distance > 0) {++iter;--distance;}
}int main(int argc, char **argv) {std::vector<int> vec {0, 1, 2, 3, 4, 5, 6};auto iter = vec.begin();advance_iter(iter, 2);std::cout << *iter << std::endl;return 0;
}
对于上述函数模板,我们使用了相同模式的enable_if,只是判断条件相反,因此,进行推断时,任何类型都不会产生二义性。大家可能会有疑问:可不可以将enable_if作为模板参数,如同下面的实现:
template <typename iterator_type>
constexpr bool is_random_access_iterator = std::is_convertible<typename std::iterator_traits<iterator_type>::iterator_category, std::random_access_iterator_tag>::value;template <typename iterator_type, typename distance_type, typename = typename std::enable_if<is_random_access_iterator<iterator_type>>::type>
void advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::random_access_iterator_tag" << std::endl;iter += distance;
}template <typename iterator_type, typename distance_type, typename = typename std::enable_if<!is_random_access_iterator<iterator_type>>::type>
void advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::input_iterator_tag" << std::endl;while (distance > 0) {++iter;--distance;}
}
答案是否定的,上面的源码无法通过编译,报错如下:
function_enable3.cpp:14:70: error: template parameter redefines default argument
template <typename iterator_type, typename distance_type, typename = typename std::enable_if<!is_random_access_iterator<iterator_type>>::type>^
function_enable3.cpp:8:70: note: previous default template argument defined here
template <typename iterator_type, typename distance_type, typename = typename std::enable_if<is_random_access_iterator<iterator_type>>::type>^
function_enable3.cpp:15:6: error: redefinition of 'advance_iter'
void advance_iter(iterator_type &iter, distance_type distance) {^
function_enable3.cpp:9:6: note: previous definition is here
void advance_iter(iterator_type &iter, distance_type distance) {^
function_enable3.cpp:26:5: error: no matching function for call to 'advance_iter'advance_iter(iter, 2);^~~~~~~~~~~~
function_enable3.cpp:15:6: note: candidate template ignored: requirement '!is_random_access_iterator<std::__wrap_iter<int *>>' was not satisfied [with iterator_type = std::__wrap_iter<int *>, distance_type = int]
void advance_iter(iterator_type &iter, distance_type distance) {^
报错原因和解决方案大家可以参考下一节 EnableIf 所之何处。现在我们需要支持距离参数为负数的情况,加入一个新的算法变体,应该如何实现?我们可以很快写出下面的代码:
//...
template <typename iterator_type>
constexpr bool is_bidirectional_iterator = std::is_convertible<typename std::iterator_traits<iterator_type>::iterator_category, std::bidirectional_iterator_tag>::value;
//...
template <typename iterator_type, typename distance_type>
typename std::enable_if<is_bidirectional_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::bidirectional_iterator_tag" << std::endl;if (distance > 0) {for ( ; distance > 0; ++iter, --distance) {}}else {for ( ; distance < 0; --iter, ++distance) {}}
}//...
然而却无法编译通过,报错如下:
function_enable2.cpp:43:5: error: call to 'advance_iter' is ambiguousadvance_iter(iter, 2);^~~~~~~~~~~~
function_enable2.cpp:13:1: note: candidate function [with iterator_type = std::__wrap_iter<int *>, distance_type = int]
advance_iter(iterator_type &iter, distance_type distance) {
^
function_enable2.cpp:30:1: note: candidate function [with iterator_type = std::__wrap_iter<int *>, distance_type = int]
advance_iter(iterator_type &iter, distance_type distance) {
^
很明显,在进行推断时出现了歧义,编译器不知道使用哪个版本的advance_iter。正确的做法是:通过让每一个函数模板的 EnableIf 条件与其它所有函数模板的条件互相排斥,可以保证对于 一组参数,最多只有一个函数模板可以在模板参数推断中胜出。详细代码如下:
//...
template <typename iterator_type>
constexpr bool is_random_access_iterator = std::is_convertible<typename std::iterator_traits<iterator_type>::iterator_category, std::random_access_iterator_tag>::value;template <typename iterator_type>
constexpr bool is_bidirectional_iterator = std::is_convertible<typename std::iterator_traits<iterator_type>::iterator_category, std::bidirectional_iterator_tag>::value;template <typename iterator_type, typename distance_type>
typename std::enable_if<is_random_access_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::random_access_iterator_tag" << std::endl;iter += distance;
}template <typename iterator_type, typename distance_type>
typename std::enable_if<!is_bidirectional_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::input_iterator_tag" << std::endl;while (distance > 0) {++iter;--distance;}
}template <typename iterator_type, typename distance_type>
typename std::enable_if<is_bidirectional_iterator<iterator_type> && !is_random_access_iterator<iterator_type>>::type
advance_iter(iterator_type &iter, distance_type distance) {std::cout << "std::bidirectional_iterator_tag" << std::endl;if (distance > 0) {for ( ; distance > 0; ++iter, --distance) {}}else {for ( ; distance < 0; --iter, ++distance) {}}
}//...
EnableIf 所之何处
std::enable_if不仅可以用于函数模板的返回类型,还可以用于没有返回类型的构造函数模板,类型转换模板,用户对模板参数进行限制。使用方法是增加一个匿名默认模板参数,如下:
#include <type_traits>
#include <iostream>
#include <string>
#include <vector>
#include <map>struct point4d {double x;double y;double z;double w;
};struct point3d {double x;double y;double z;
};struct point2d {double x;double y;operator point3d() { return point3d(); }
};template <typename T>
class line
{
public:line(void) : m_points() {}template <typename U, typename = typename std::enable_if<std::is_same<typename U::value_type, T>::value>::type>line(const U &arg) {}template <typename V, typename = typename std::enable_if<std::is_convertible<T, V>::value>::type>operator line<V>() const { return line<V>(); }private:std::vector<T> m_points;};int main(int arc, char ** argv)
{std::vector<point2d> pt2_vect;line<point2d> line1(pt2_vect);//std::map<int, point2d>的value_type为std::pair<int, point2d>,不符合要求,失败//std::map<int, point2d> pt2_map;//line<point2d> line2(pt2_map);line<point3d> line3 = line1;//point2d不能转换为point2d,不符合要,失败//line<point4d> line4 = line1;return 0;
}
如果想添加一个如下的构造函数模板,编译会报错“constructor cannot be redeclared”。
template <typename U, typename = typename std::enable_if<std::is_same<U, std::map<int, T>>::value>::type>line(const U &arg) {}
报错的原因是这两个模板唯一的区别是默认模板参数,但是在判断两个模板是否相同的时候却又不会考虑默认模板参数,解决方案是再增加一个默认的模板参数,如下:
template <typename U, typename = typename std::enable_if<std::is_same<U, std::map<int, T>>::value>::type, typename = void>line(const U &arg) {}
Concepts
使用concept,比enable_if更加简洁,直接(c++20以后),源码如下:
#include <type_traits>
#include <iostream>
#include <cstring>template <typename T>
concept fundamental = std::is_fundamental<T>::value && !std::is_void<T>::value && !std::is_null_pointer<T>::value;template <typename T>
requires fundamental<T>
bool compare(const T a, const T b) {std::cout << "fundamental" << std::endl;return a < b;
}template <typename T>
concept number_pointer = std::is_pointer<T>::value && !std::is_same<T, char *>::value;template <typename T>
requires number_pointer<T>
bool compare(const T a, const T b) {std::cout << "number_pointer" << std::endl;return *a < *b;
}template <typename T>
concept cstr = std::is_pointer<T>::value && std::is_same<T, char *>::value;template <typename T>
requires cstr<T>
bool compare(const T a, const T b) {std::cout << "cstr" << std::endl;return strcmp(a, b) < 0;
}int main(int argc, char **argv) {int a = 10;int b = 100;compare(a, b);int *pa = &a;int *pb = &b;compare(pa, pb);char *str0 = (char *)"hello";char *str1 = (char *)"world";compare(str0, str1);return 0;
}
类的特化
禁用/启用模板
通过enanble_if禁用/启用模板,可以实现类模板的重载,源码如下:
#include <type_traits>
#include <iostream>template <typename T, typename = void>
struct compare {bool operator()(const T x, const T y) { return x < y; }constexpr static int value = 1;
};template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value && !std::is_same<T, char *>::value>::type> {bool operator()(const T x, const T y) { return *x < *y; }constexpr static int value = 2;
};template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value && std::is_same<T, char *>::value>::type> {bool operator()(const T x, const T y) { return strcmp(x, y) < 0; }constexpr static int value = 3;
};int main(int argc, char **argv) {std::cout << compare<int>::value << std::endl;std::cout << compare<int *>::value << std::endl;std::cout << compare<char *>::value << std::endl;return 0;
}
但是需要注意,偏特化模板类的条件参数必须能够彼此区分,像下面的实现,条件参数无法做到彼此区分:
template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value>::type> {bool operator()(const T x, const T y) { return *x < *y; }constexpr static int value = 2;
};template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value && std::is_same<T, char *>::value>::type> {bool operator()(const T x, const T y) { return strcmp(x, y) < 0; }constexpr static int value = 3;
};
当模板参数类型为char*,enanble_if条件std::is_pointer<T>::value和std::is_pointer<T>::value && std::is_same<T, char *>::value都为true,编译器不知道该调用哪个模板,因此报出如下的错误:
class_enable.cpp:25:18: error: ambiguous partial specializations of 'compare<char *>'std::cout << compare<char *>::value << std::endl;^
class_enable.cpp:11:8: note: partial specialization matches [with T = char *]
struct compare<T, typename std::enable_if<std::is_pointer<T>::value>::type> {^
class_enable.cpp:17:8: note: partial specialization matches [with T = char *]
struct compare<T, typename std::enable_if<std::is_pointer<T>::value && std::is_same<T, char *>::value>::type> {
解决方案,就是为第一个偏特化实现enable_if条件增加!std::is_same<T, char *>::value,这样两个偏特化模板彼此互斥,必定能够区分彼此。
下面的实现,虽然能够区分两个偏特化模板,但执行结果却不符合预期。
template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value>::type> {bool operator()(const T x, const T y) { return *x < *y; }constexpr static int value = 2;
};template <typename T>
struct compare<T, typename std::enable_if<std::is_pointer<T>::value && std::is_same<T, char *>::value, T>::type> {bool operator()(const T x, const T y) { return strcmp(x, y) < 0; }constexpr static int value = 3;
};
为什么编译其选择了第一个偏特化类模板? 自己确实没有搞明白,欢迎知道的朋友留言
标签派发
同样的,标签派发也可以用于在不同的类模板特化之间做选择,下面是类模板使用标签派发的一个例子:
#include <type_traits>
#include <iostream>struct integer_tag {};
struct integer_pointer_tag {};
struct str_pointer_tag {};
struct floating_tag{};struct integer {using tag = integer_tag;int i = 0;
};struct integer_pointer {using tag = integer_pointer_tag;int *pi = nullptr;
};struct str_pointer {using tag = str_pointer_tag;char *pstr = nullptr;
};struct floating {using tag = floating_tag;float f;
};template <typename T, typename = typename T::tag>
struct compare;template <typename T>
struct compare<T, integer_tag> {bool operator()(const T x, const T y) {std::cout << "integer" << std::endl;return x.i < y.i;}
};template <typename T>
struct compare<T, integer_pointer_tag> {bool operator()(const T x, const T y) {std::cout << "integer ptr" << std::endl;return *(x.pi) < *(y.pi);}
};template <typename T>
struct compare<T, str_pointer_tag> {bool operator()(const T x, const T y) {std::cout << "string ptr " << std::endl;return strcmp(x.pstr, y.pstr) < 0;}
};template <typename T>
bool compare_(const T x, const T y) {return compare<T>()(x, y);
}int main(int argc, char **argv) {integer a, b;a.i = 10;b.i = 5;std::cout << (compare_(a, b) ? "true" : "false") << std::endl;str_pointer str0, str1;str0.pstr = (char *)"1";str1.pstr = (char *)"2";std::cout << (compare_(str0, str1) ? "true" : "false") << std::endl;/*floating c, d;c.f = 3.14;d.f = 2.17;std::cout << (compare_(c, d) ? "true" : "false") << std::endl;*/return 0;
}
关于上面的例子,还有一种写法,如下:
#include <type_traits>
#include <iostream>template <typename ...Types>
struct match_overloads;template <>
struct match_overloads<> {static void match(...);
};template <typename T, typename ...Rest>
struct match_overloads<T, Rest...> : public match_overloads<Rest...> {static T match(T);using match_overloads<Rest...>::match;
};template <typename T, typename ...Types>
struct best_match_in_set {using type = decltype(match_overloads<Types...>::match(std::declval<T>()));
};template <typename T, typename ...Types>
using best_match_in_set_t = typename best_match_in_set<T, Types...>::type;struct integer_tag {};
struct integer_pointer_tag {};
struct str_pointer_tag {};
struct floating_tag{};struct integer {using tag = integer_tag;int i = 0;
};struct integer_pointer {using tag = integer_pointer_tag;int *pi = nullptr;
};struct str_pointer {using tag = str_pointer_tag;char *pstr = nullptr;
};struct floating {using tag = floating_tag;float f;
};template <typename T, typename tag = best_match_in_set_t<typename T::tag, integer_tag, integer_pointer, str_pointer_tag>>
struct compare;template <typename T>
struct compare<T, integer_tag> {bool operator()(const T x, const T y) {std::cout << "integer" << std::endl;return x.i < y.i;}
};template <typename T>
struct compare<T, integer_pointer_tag> {bool operator()(const T x, const T y) {std::cout << "integer ptr" << std::endl;return *(x.pi) < *(y.pi);}
};template <typename T>
struct compare<T, str_pointer_tag> {bool operator()(const T x, const T y) {std::cout << "string ptr " << std::endl;return strcmp(x.pstr, y.pstr) < 0;}
};template <typename T>
bool compare_(const T x, const T y) {return compare<T>()(x, y);
}int main(int argc, char **argv) {integer a, b;a.i = 10;b.i = 5;std::cout << (compare_(a, b) ? "true" : "false") << std::endl;str_pointer str0, str1;str0.pstr = (char *)"1";str1.pstr = (char *)"2";std::cout << (compare_(str0, str1) ? "true" : "false") << std::endl;/*floating c, d;c.f = 3.14;d.f = 2.17;std::cout << (compare_(c, d) ? "true" : "false") << std::endl;*/return 0;
}
两种实现能够实现标签派发,但新的实现能够显示的指出标签派发支持哪些tag。
实例化安全的模板
std::enable_if技术本质:只有满足模板参数满足某些条件,才可以使用某个模板或某个偏特化模板。
将模板用到的所有模板参数操作都使用enable_if进行条件判断,则模板的实例化永远不会失败,因为没有提供enable_if所需操作的模板参数会导致推断错误,而不会在可能会出错的情况下继续实例化。这一类模板被称为“实例化安全”的模板。
举个例子,实现一个函数,返回两个变量中较小的一个。毫无疑问,我们会实现一个函数模板,并且把变量类型定义为模板参数,以便支持各种类型。很明显,该函数模板中的模板参数要满足以下需求:
- 支持比较操作符<
- 比较操作符<的返回结果为bool值
我们先不限制这两点需求,实现函数,如下:
#include <type_traits>template <typename T>
T min(T const &x, T const &y) {if (y < x)return y;return x;
}struct X1 {};
bool operator<(X1 const &, X1 const &) { return true; }struct X2{};
bool operator<(X2, X2) { return true; }struct X3{};
bool operator<(X3 &, X3 &) { return true; }struct X4{};struct X5{};
struct BoolConvertible {operator bool() const { return true; }
};
BoolConvertible operator<(X5 const &, X5 const &) { return BoolConvertible(); }struct X6{};
struct NotBoolConvertible {};
NotBoolConvertible operator<(X6 const &, X6 const &) { return NotBoolConvertible(); }struct X7{};
struct BoolLike {explicit operator bool() const { return true; }
};
BoolLike operator<(X7 const &, X7 const &) { return BoolLike(); }int main(int argc, char **argv) {min(X1(), X1());min(X2(), X2());min(X3(), X3());min(X4(), X4());min(X5(), X5());min(X6(), X6());min(X7(), X7());return 0;
}
执行编译,编译器报错如下:
initial_safe_template.cpp:35:11: error: invalid operands to binary expression ('const X3' and 'const X3')if (y < x)~ ^ ~
initial_safe_template.cpp:71:5: note: in instantiation of function template specialization 'min<X3>' requested heremin(X3(), X3());^
initial_safe_template.cpp:42:6: note: candidate function not viable: no known conversion from 'const X3' to 'const X1' for 1st argument
bool operator<(X1 const &, X1 const &) { return true; }^
initial_safe_template.cpp:45:6: note: candidate function not viable: no known conversion from 'const X3' to 'X2' for 1st argument
bool operator<(X2, X2) { return true; }^
initial_safe_template.cpp:48:6: note: candidate function not viable: 1st argument ('const X3') would lose const qualifier
bool operator<(X3 &, X3 &) { return true; }^
initial_safe_template.cpp:56:17: note: candidate function not viable: no known conversion from 'const X3' to 'const X5' for 1st argument
BoolConvertible operator<(X5 const &, X5 const &) { return BoolConvertible(); }^
initial_safe_template.cpp:60:20: note: candidate function not viable: no known conversion from 'const X3' to 'const X6' for 1st argument
NotBoolConvertible operator<(X6 const &, X6 const &) { return NotBoolConvertible(); }^
initial_safe_template.cpp:66:10: note: candidate function not viable: no known conversion from 'const X3' to 'const X7' for 1st argument
BoolLike operator<(X7 const &, X7 const &) { return BoolLike(); }^
initial_safe_template.cpp:35:11: error: invalid operands to binary expression ('const X4' and 'const X4')if (y < x)~ ^ ~
initial_safe_template.cpp:72:5: note: in instantiation of function template specialization 'min<X4>' requested heremin(X4(), X4());^
initial_safe_template.cpp:42:6: note: candidate function not viable: no known conversion from 'const X4' to 'const X1' for 1st argument
bool operator<(X1 const &, X1 const &) { return true; }^
initial_safe_template.cpp:45:6: note: candidate function not viable: no known conversion from 'const X4' to 'X2' for 1st argument
bool operator<(X2, X2) { return true; }^
initial_safe_template.cpp:48:6: note: candidate function not viable: no known conversion from 'const X4' to 'X3 &' for 1st argument
bool operator<(X3 &, X3 &) { return true; }^
initial_safe_template.cpp:56:17: note: candidate function not viable: no known conversion from 'const X4' to 'const X5' for 1st argument
BoolConvertible operator<(X5 const &, X5 const &) { return BoolConvertible(); }^
initial_safe_template.cpp:60:20: note: candidate function not viable: no known conversion from 'const X4' to 'const X6' for 1st argument
NotBoolConvertible operator<(X6 const &, X6 const &) { return NotBoolConvertible(); }^
initial_safe_template.cpp:66:10: note: candidate function not viable: no known conversion from 'const X4' to 'const X7' for 1st argument
BoolLike operator<(X7 const &, X7 const &) { return BoolLike(); }^
initial_safe_template.cpp:35:9: error: value of type 'NotBoolConvertible' is not contextually convertible to 'bool'if (y < x)^~~~~
initial_safe_template.cpp:74:5: note: in instantiation of function template specialization 'min<X6>' requested heremin(X6(), X6());^
3 errors generated.
然后,在使用enable_if对上述两点需求做出限制,新代码如下:
#include <type_traits>template <typename T1, typename T2>
class has_less {template <typename T>struct identity;template <typename U1, typename U2>static std::true_type test(identity<decltype(std::declval<U1>() < std::declval<U2>())>*);template <typename U1, typename U2>static std::false_type test(...);
public:static constexpr bool value = decltype(test<T1, T2>(nullptr))::value;
};template <typename T1, typename T2, bool has_less>
struct less_result_impl {using type = decltype(std::declval<T1>() < std::declval<T2>());
};template <typename T1, typename T2>
struct less_result_impl<T1, T2, false> {};template <typename T1, typename T2>
struct less_result_t : less_result_impl<T1, T2, has_less<T1,T2>::value> {};template <typename T1, typename T2>
using less_result = typename less_result_t<T1, T2>::type;template <typename T>
typename std::enable_if<std::is_convertible<less_result<T const &, T const &>, bool>::value, T const &>::type
min(T const &x, T const &y) {if (y < x)return y;return x;
}//......
编译后,代码报错如下:
initial_safe_template.cpp:71:5: error: no matching function for call to 'min'min(X3(), X3());^~~
initial_safe_template.cpp:34:1: note: candidate template ignored: substitution failure [with T = X3]: no type named 'type' in 'less_result_t<const X3 &, const X3 &>'
min(T const &x, T const &y) {
^
initial_safe_template.cpp:72:5: error: no matching function for call to 'min'min(X4(), X4());^~~
initial_safe_template.cpp:34:1: note: candidate template ignored: substitution failure [with T = X4]: no type named 'type' in 'less_result_t<const X4 &, const X4 &>'
min(T const &x, T const &y) {
^
initial_safe_template.cpp:74:5: error: no matching function for call to 'min'min(X6(), X6());^~~
initial_safe_template.cpp:34:1: note: candidate template ignored: requirement 'std::is_convertible<NotBoolConvertible, bool>::value' was not satisfied [with T = X6]
min(T const &x, T const &y) {
^
initial_safe_template.cpp:75:5: error: no matching function for call to 'min'min(X7(), X7());^~~
initial_safe_template.cpp:34:1: note: candidate template ignored: requirement 'std::is_convertible<BoolLike, bool>::value' was not satisfied [with T = X7]
min(T const &x, T const &y) {
^
4 errors generated.
相对于第一次的编译错误,第二次显然更加简洁直接。
关于类型X7需要特别说明一下,如果min是普通函数,即如下的定义:
X7 const & min(X7 const &x, X7 const &y) {if (y < x)return y;return x;
}
min(X7(), X7())是可以通过编译的。由于BooLike到bool的转换必须是显示的,在某些情况下,比如控制语句(if,while,for 以及 do)的布尔型条件,内置的!,&&以及||运算符,还有三元运算符?:,这种转换是有效的(即显示向bool的转换是可以被隐式调用的),但enable_if指定的条件比我们实际需要的条件更加严格,认定这种转换无效,导致实例化失败。
为了解决 min()中这一由实例化安全带来的问题,我们需要一个可以判断某个类型是否是“语 境上可以转换成 bool”的萃取技术。新的min源码如下:
//......
template <typename T>
class is_contextual_bool {template <typename U>struct Identity;template<typename U>static std::true_type test(Identity<decltype(std::declval<U>()? 0 : 1)>*);template<typename U>static std::false_type test(...);
public:static constexpr bool value = decltype(test<T>(nullptr))::value;
};template <typename T>
typename std::enable_if<is_contextual_bool<less_result<T const &, T const &>>::value, T const &>::type
min(T const &x, T const &y) {if (y < x)return y;return x;
}
//......
相关文章:
基于类型属性的重载
算法重载 在一个泛型算法中引入更为特化的变体,这种设计和优化方式称为算法特化。之所以需要算法特化,原因有二: 针对特定类型使用更加合理的实现,对于const char *,less的第二个实现更加合理 template <typename…...
对称加密算法和非对称加密算法
在这个互联网普及的时代,在不同终端对敏感甚至机密数据进行传输是非常常见的场景,但是如何保证数据传输过程的安全性和高效性是一个值得深入探讨的问题。 为此,伟大的人类研究出了多种加密算法,我们可以大致将其分为两类…...
工程数字建造管理系统平台有哪些?好的数字建造管理系统推荐
一、什么是工程数字建造管理系统平台? 工程数字建造管理系统平台是一种集成了先进信息技术(如云计算、大数据、物联网等)的综合性管理工具,它旨在通过数字化手段提升工程建造全过程的管理效率和决策水平。这一平台不仅覆盖了工程…...
CMake ERROR: arm-none-eabi-gcc is not able to compile a simple test program.
用 cmake 构建 STM32 工程问题【已解决】 环境信息 os: ubuntu22.04gcc: arm-none-eabi-gcc (Arm GNU Toolchain 13.2.rel1 (Build arm-13.7)) 13.2.1 20231009cmake: cmake version 3.22.1ninja: 1.10.1 问题 log [main] 正在配置项目: Olidy [driver] 删除 /home/pomegr…...
容器主机CPU使用率突增问题一则
关键词 LINUX、文件系统crontab 、mlocate根目录使用率 There are many things that can not be broken! 如果觉得本文对你有帮助,欢迎点赞、收藏、评论! 一、问题现象 业务一台容器服务器,近期经常收到cpu不定期抖动告警&#x…...
CTFshow【命令执行】web29-web40 做题笔记
web29----过滤关键字 正则匹配,过滤flag,可以使用通配符绕过 先查看目录 使用通配符?查看flag 点击查看源代码 web30---过滤函数和关键字 看到过滤了system执行系统命令和flag,php关键字 找一个与其功能差不多的执行函数passthr…...
L2正则化:优化模型的平滑之道
常见的正则化方法1. **L1正则化(Lasso)**1.1基本原理1.2特点1.3数学推导1.4参数更新1.5选择合适的正则化系数1.6优点1.7缺点1.8实际应用中的注意事项1.9示例 2. **L2正则化(Ridge)**L2正则化的定义L2正则化如何防止过拟合1. **限制…...
Golang 的 GMP 调度机制常见问题及解答
文章目录 Golang GMP 调度模型详解常见问题基础概念1. GMP 各组件的作用是什么?2. 为什么 Go 需要自己的调度器?3. GOMAXPROCS 的作用是什么? 调度流程4. Goroutine 如何被调度到 M 上执行?5. 系统调用会阻塞整个线程吗࿱…...
使用VSCODE导致CPU占用率过高的处理方法
1:cpptools 原因:原因是C/C会在全局搜索文件,可以快速进行跳转;当打开的文件过大,全局搜索文件会占用大量CPU; 处理方法: 1:每次只打开小文件夹; 2:打开大文…...
17--华为防火墙智能选路全解:网络世界的智能导航系统
华为防火墙智能选路全解:网络世界的智能导航系统 开篇故事:快递小哥的烦恼与网络世界的相似性 想象你是个快递站站长,每天要处理来自全国各地的包裹。突然某天遇到: 🚚 双11爆仓:如何把包裹最快送到客户手…...
CSS-BFC(块级格式化上下文)
一、BFC 的本质理解(快递仓库比喻) 想象一个快递分拣仓库(BFC容器),这个仓库有特殊的规则: 仓库内的包裹(内部元素)摆放不影响其他仓库包裹必须整齐堆叠,不能越界不同仓…...
Java 大视界 -- Java 大数据在智慧港口集装箱调度与物流效率提升中的应用创新(159)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
ZygoPlanner:一种基于图形的三阶段框架,用于颧骨种植体植入的最佳术前规划|文献速递-医学影像人工智能进展
Title 题目 ZygoPlanner: A three-stage graphics-based framework for optimal preoperative planning of zygomatic implant placement ZygoPlanner:一种基于图形的三阶段框架,用于颧骨种植体植入的最佳术前规划 01 文献速递介绍 1.1 研究背景 颧…...
【2.项目管理】2.4 Gannt图【甘特图】
甘特图(Gantt)深度解析与实践指南 📊 一、甘特图基础模板 项目进度表示例 工作编号工作名称持续时间(月)项目进度(周)1需求分析3▓▓▓░░░░░░░2设计建模3░▓▓▓░░░░░░3编码开发3.5░░░▓▓▓▓░░…...
python学习笔记(1)
为什么要学python 目前在研究网站的搭建,想通过python搭建一个我的世界资源买卖的平台,然后就开始研究python了,其实这不是我第一次研究python了,之前学的都不咋样,现在温故而知新,好好研究一下python。 Python的变量 在此之前先简单的介绍一下变量,给第一次接触的小…...
刚刚整理实测可用的股票数据API接口集合推荐:同花顺、雅虎API、智兔数服、聚合数据等Python量化分析各项数据全面丰富
在金融科技高速发展的今天,股票API接口已成为开发者、量化交易者和金融从业者的核心工具之一。它通过标准化的数据接口,帮助用户快速获取实时或历史市场数据,为投资决策、策略回测和金融应用开发提供支持。本文将深入解析股票API的核心功能、…...
2025 年吉林省燃气企业从业人员考试:实用备考攻略与考试提分要点
2025 年吉林省燃气企业从业人员考试报名通过吉林燃气行业管理系统。报名资料包含企业的环保达标证明(燃气行业涉及环保要求)、个人的岗位任职证明等。实用备考攻略是,关注吉林在燃气分布式能源系统建设方面的进展,结合《燃气冷热电…...
dubbo http流量接入dubbo后端服务
简介 dubbo协议是基于TCP的二进制私有协议,更适合作为后端微服务间的高效RPC通信协议,也导致dubbo协议对于前端流量接入不是很友好。在dubo框架中,有两种方式可以解决这个问题: 多协议发布【推荐】,为dubbo协议服务暴…...
自动驾驶04:点云预处理03
点云组帧 感知算法人员在完成点云的运动畸变补偿后,会发现一个问题:激光雷达发送的点云数据包中的点云数量其实非常少,完全无法用来进行后续感知和定位层面的处理工作。 此时,感知算法人员就需要对这些数据包进行点云组帧的处理…...
小程序中过滤苹果设备中的表情(即 emoji)
在小程序中过滤苹果设备中的表情(即 emoji),通常需要考虑以下几个方面:识别 emoji、处理用户输入、以及在显示或存储时进行过滤。以下是具体的实现思路和步骤: 1. 理解苹果中的表情(Emoji) 苹果…...
软件性能测试中的“假阳性”陷阱
软件性能测试中的“假阳性”陷阱主要表现为错误警报频繁、资源浪费严重、测试可信度降低。其中,错误警报频繁是最常见且最严重的问题之一,“假阳性”现象会导致开发团队在解决不存在的问题上花费大量时间。据行业调查显示,超过30%的性能优化成…...
现代优雅品牌杂志包装徽标设计衬线英文字体安装包 Relish – Aesthetic Font
CS Relish – 美学字体:优雅与现代简约的结合 永恒的现代 Serif 字体 CS Relish 是一种现代衬线字体,将极简主义美学与精致精致融为一体。凭借其时尚、干净的字体和平衡的结构,它给人一种优雅和现代的印象。这款字体专为那些欣赏微妙和优雅的…...
《Oracle服务进程精准管控指南:23c/11g双版本内存优化实战》 ——附自动化脚本开发全攻略
正在学习或者是使用 Oracle 数据库的小伙伴,是不是对于那个一直启动且及其占用内存的后台进程感到烦躁呢?而且即使是手动去开关也显得即为麻烦,所以基于我之前所学习到的方法,我在此重新整理,让大家动动手指就能完成开…...
《寒门枭雄传》章回目录与核心故事设计(36回)
《寒门枭雄传》章回目录与核心故事设计(36回) 主线:寒门崛起→权力异化→制度轮回 核心冲突:个人奋斗 vs 制度性压迫 第一卷京口草鞋摊的野望(第1-12回) 主题:寒门之困始于生存,终…...
C语言学习笔记(抱佛脚版)
毕业一年,发现记性是真的差,每次想起之前的知识总是想不全,看别人写的资料也懵懵懂懂。于是我索性自己再学一遍,并且记录一下。希望对你们也有所帮助。 正片开始! 前面的什么if for都不难理解,嵌套的话也…...
DeepSeek-V3-0324 模型发布:开源 AI 性能再攀高峰,推理与编码能力逼近顶级闭源模型
2025 年 3 月 24 日,国内 AI 公司深度求索(DeepSeek)悄然推出 V3 模型的升级版本 DeepSeek-V3-0324。尽管此次更新并非市场期待的 V4 或 R2 版本,但其在推理速度、编码能力、数学推理及开源生态上的突破,仍迅速引发全球…...
清晰易懂的Cursor实现AI编程从安装到实战TodoList开发
一、Cursor简介与安装部署 什么是Cursor? Cursor是一款基于AI的智能代码编辑器,它集成了强大的AI编程助手功能,能够通过自然语言交互帮助开发者生成、优化和调试代码。与传统的代码编辑器不同,Cursor可以理解你的编程意图&#…...
(二) 深入了解AVFoundation - 播放:AVFoundation 播放基础入门
引言 AVFoundation 是 Apple 提供的强大多媒体框架,支持音视频播放、录制、处理等功能。在 iOS 开发中,AVFoundation 是实现视频播放的核心技术之一。 本篇文章将简单介绍如何使用 AVPlayer、AVPlayerItem 和 AVPlayerLayer 进行视频播放,并…...
重磅推出稳联技术Profinet转CANopen网关智能工厂解决方案!
重磅推出稳联技术Profinet转CANopen网关智能工厂解决方案! 稳联技术Profinet转CANopen网关应运而生——它如同一座智能桥梁☺,打通两大主流工业协议,让异构网络无缝互联,助您释放设备潜力,实现真正的“万物互联”&…...
【问题解决】Linux安装conda修改~/.bashrc配置文件后,root 用户下显示 -bash-4.2#
问题描述 在Linux安装conda下的python环境时候,修改了~/.bashrc文件,修改完成后,再次进入服务器后,登录时候显示的不是正常的[rootlocalhost ~]#,而是-bash-4.2# 原因分析: 网上原因有:/root下…...
关于deepseek
DeepSeek:领先的人工智能研究与创新公司 公司简介 DeepSeek(深度求索)是一家专注于人工智能(AI)技术研发的创新公司,致力于推动大模型、自然语言处理(NLP)、机器学习(M…...
EtherCAT转ProfiNet协议转换网关构建西门子PLC与海克斯康机器人的冗余通信链路
一、案例背景 某电子制造企业的5G通信模块组装线,采用西门子S7-1200PLC(ProfiNet主站)进行产线调度,而精密组装工序由3台海克斯康工业机器人(EtherCAT从站)完成。由于协议差异,机器人动作与PLC…...
李宏毅机器学习笔记(1)—机器学习基本概念+深度学习基本概念
机器学习基本概念 1、获取模型 步骤 1.1、假定未知函数 带未知参数的函数 1.2、定义损失函数 真实值:label MAE MSE 几率分布,cross-entropy? 1.3、优化 单独考虑一个参数 让损失函数最小,找导数为零的点 单独考虑w,w…...
RAG生成中的多文档动态融合及去重加权策略探讨
目录 RAG生成中的多文档动态融合及去重加权策略探讨 一、RAG生成概述 二、多文档动态融合策略 1. 拼接与分段编码 2. 独立编码与后续融合 3. 基于查询的动态加权 三、检索结果的去重与加权策略 1. 去重策略 2. 加权策略 四、实践中的挑战与思考 五、结语 RAG生成中的…...
对匿名认证的理解
概述:在 Spring Security 中,** 匿名认证(Anonymous Authentication)** 是一种特殊的认证机制,用于处理未提供有效凭证的请求。 匿名认证的本质 目的:允许未认证用户访问特定资源。原理: 当请求…...
leetcoed0044. 通配符匹配 hard
1 题目:通配符匹配 官方难度:难 给你一个输入字符串 (s) 和一个字符模式 ( p ) ,请你实现一个支持 ‘?’ 和 ‘*’ 匹配规则的通配符匹配: ‘?’ 可以匹配任何单个字符。 ‘*’ 可以匹配任意字符序列(包括空字符序…...
航拍数据集汇总,覆盖车辆/船舶检测/物体评估/城市景观……
随着无人机的普及化和计算机视觉技术的迅猛发展,无人机航拍作为一种创新的摄影方式,正以前所未有的速度走进大众视野。它打破了传统拍摄的局限,为我们开启了「上帝视角」。航拍硬件性能逐渐逼近物理极限,算法优化的难度也日益增大…...
【SECS】初识SECS协议
【SECS】初识SECS协议 基本知识流和功能函数数量官方文件中缩写标注正常是不是都是主机向设备端?对数据信息中第1字节第1-2位官网介绍 S1F1双向指令说明测试H发起端E发起端 参考资料 基本知识 SECS(SEMI Equipment Communications Standard)即半导体设…...
RL基础以及AlphaGo、AlphaGo Zero原理
RL基础 Q价值函数和状态价值函数 Action-Value function: Q ( s , a ) Q(s, a) Q(s,a)是agent在状态s下执行某一个动作(如向上走),所获得的及时奖励和未来折扣的累计奖励 State-Value function: V ( s ) V(s) V(s)是…...
Android R adb remount 调用流程
目的:调查adb remount 与adb shell进去后执行remount的差异 调试方法:添加log编译adbd,替换system\apex\com.android.adbd\bin\adbd 一、调查adb remount实现 关键代码:system\core\adb\daemon\services.cpp unique_fd daemon_service_to…...
uvm sequence
UVM Sequence 是验证环境中生成和控制事务(Transaction)流的核心机制,它通过动态生成、随机化和调度事务,实现灵活多样的测试场景。以下是Sequence的详细解析: Sequence 的核心作用 事务流生成:通过 uvm_s…...
Java 代理(一) 静态代理
学习代理的设计模式的时候,经常碰到的一个经典场景就是想统计某个方法的执行时间。 1 静态代理模式的产生 需求1. 统计方法执行时间 统计方法执行时间,在很多API/性能监控中都有这个需求。 下面以简单的计算器为例子,计算加法耗时。代码如下…...
《初级社会工作者》考试题,附答案解析
一、单项选择题(共 60 题,每题 1 分) 1. 社会工作者在社区中开展针对留守儿童的支持小组活动,发现一名儿童因父母长期外出务工而产生严重的分离焦虑。此时,社会工作者应首先采取的介入策略是( )…...
网盘解析工具1.3.0,修改了一些bug,建议更新到此版本
最近问题反馈比较多,本来想着周末再更新了来着,但是账号黑的实在太快了。排查了下应该是某度网盘的一个接口缺少了一些参数,导致一直进黑名单。所幸参数不难找,新版本已经修复了,建议大家赶紧更新到1.3.0版本ÿ…...
Multi-Stage Progressive Image Restoration论文阅读
摘要 图像复原任务在恢复图像时需要在空间细节与高层语境化信息之间取得复杂的平衡。本文提出了一种新颖的协同设计方法,能够最优地平衡这些竞争目标。我们的核心方案是一种多阶段架构,通过逐步学习退化输入的复原函数,将整体恢复过程分解为…...
了解图像质量评价指标PSNR
一、PSNR是什么 1.1 定义与数学公式 峰值信噪比(Peak Signal-to-Noise Ratio,PSNR)是数字图像处理领域最经典的客观质量评价指标之一。其核心思想是通过计算原始图像与失真图像之间的均方误差(MSE)来衡量失真程度&am…...
C++概述
1 什么是面向对象】 概念上来说:就是以对象(具体的变量)为导向的编程思路 专注于:一个对象具体能实现哪些过程(哪些功能) 面向对象 n * 面向过程 结论:面向对象需要做的事情 1:我们要想清楚,我们现在需要编写一个…...
axios文件下载使用后端传递的名称
java后端通过HttpServletResponse 返回文件流 在Content-Disposition中插入文件名 一定要设置Access-Control-Expose-Headers,代表跨域该Content-Disposition返回Header可读,如果没有,前端是取不到Content-Disposition的,可以在统…...
如何让 history 记录命令执行时间?Linux/macOS 终端时间戳设置指南
引言:你真的会用 history 吗? 有没有遇到过这样的情况:你想回顾某个重要命令的执行记录,却发现 history 只列出了命令序号和内容,根本没有时间戳?这在运维排查、故障分析、甚至审计时都会带来极大的不便。 想象一下,你在服务器上误删了某个文件,但不知道具体是几点执…...
使用LLaMAFactory微调Qwen大模型
一、环境配置与工具安装 1. 硬件要求 GPU:至少1块NVIDIA GPU(推荐RTX 4090/A100/H100,显存≥16GB)。内存:≥64GB系统内存。存储:≥100GB硬盘空间用于模型与数据集存储。2. 软件依赖 Python 3.8+:需安装CUDA支持的PyTorch版本(如torch==2.0.1+cu117)。 依赖库:通过以…...