当前位置: 首页 > news >正文

基于类型属性的重载

算法重载

        在一个泛型算法中引入更为特化的变体,这种设计和优化方式称为算法特化。之所以需要算法特化,原因有二:

  • 针对特定类型使用更加合理的实现,对于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 ambiguous
    advance_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 here
        min(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 here
        min(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 here
        min(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;
    }
    //......

     

    相关文章:

  • 标准库中有uint32_t类型吗?
  • 深入剖析Redis分布式锁:Redlock算法源码解读与实战
  • C++修炼:string类的使用
  • 前端Vue
  • 【HCIA-网工探长】12:生成树笔记
  • Python使用ccplot绘制CALIPSO L1B后向散射
  • C# 异步方法设计指南:何时使用 await 还是直接返回 Task?
  • C++ 字符处理、编码格式
  • 20250328易灵思FPGA的烧录器FT4232_DL的驱动安装
  • postgresql+patroni+etcd高可用安装
  • unity 截图并且展现在UI中
  • turtle的九个使用
  • 【数据分享】基于联合国城市化程度框架的全球城市边界数据集(免费获取/Shp格式)
  • Spring 拦截器(Interceptor)与过滤器(Filter)对比
  • 51c深度学习~合集4
  • 【学Rust写CAD】16 零标记类型(zero.rs)
  • linux scp复制多层级文件夹到另一服务器免密及脚本配置
  • 数据库基础(聚合函数 分组 排序)
  • 大型语言模型的秘密:思考链长度与提示格式的魔力
  • mmaction2的mmcv依赖安装教程
  • 常州专业网站建设公司/什么是网络营销与直播电商
  • 登录企业网站管理系统/广州网络推广平台
  • 如何做网站需求表格清单/上海网站建设推广服务
  • 如何解决旅游网站建设问题/站长工具之家seo查询
  • 阿里云主机怎么做两个网站/网页制作html代码
  • 广州网站外贸推广/成都关键词优化排名