[C++ Template]深入模板--特化与重载

2023-11-12

目录

第12章 特化与重载

12.2 重载函数模板

12.2.1 签名

12.2.2 重载的函数模板的局部排序

12.2.3 正式的排序原则

12.2.4 模板和非模板

12.3 显式特化

12.3.1 全局的类模板特化

12.3.2 全局的函数模板特化

12.3.3 全局成员特化

12.4 局部的类模板特化


第12章 特化与重载

目前为止,我们已经知道了:C++模板如何使一个泛型定义扩展成一些相关的类家族或者函数家族。虽然这是一个功能很强大的机制,但该机制并非适合于所有的情况;在一些情况下,这种泛型操作就不是特定模板参数替换的最佳选择。

 

12.2 重载函数模板

两个同名的函数模板可以同时存在,还可以对它们进行实例化,使它们具有相同的参数类型。下面是另一个简单的例子:

template<typename T>
int f(T)
{
    return 1;
}
template<typename T>
int f(T*)
{
    return 2;
}

如果我们用int*来替换第1个模板的T,用int来替换第2个模板的T,那么将会获得两个具有相同参数类型(和返回类型)的同名函数。也就是说,不仅是同名模板可以同时存在,它们各自的实例化体也可以同时存在,即使这些实例化体具有相同的参数类型和返回类型。

 

12.2.1 签名

只要具有不同的签名,两个函数就可以在同一个程序中同时存在。我们对函数的签名定如下

1.非受限函数的名称(或者产生自函数模板的这类名称)。

2.函数名称所属的类作用域或者名字空间作用域;如果函数名称是具有内部链接的,还包括该名称声明所在的翻译单元。\

3.函数的const、volatile或者const volatile限定符(前提是它是一个具有这类限定符的成员函数)。

4.函数参数的类型(如果这个函数是产生自函数模板的,那么指的是模板参数被替换之前的类型)。

5.如果这个函数是产生自函数模板,那么包括它的返回类型。

6.如果这个函数是产生自函数模板,那么包括模板参数和模板实参。

这就意味着:从原则上讲,下面的模板和它们的实例化体可以在同个程序中同时存在:

template<typename T1, typename T2>
void f1(T1, T2);
template<typename T1, typename T2>
void f1(T2, T1);
template<typename T>
long f2(T);
template<typename T>
char f2(T);

然而,如果上面这些模板是在同一个作用域中进行声明的话,我们可能不能使用某些模板,因为实例化过程可能会导致重载二义性。例如:

template<typename T1, typename T2>
void f1(T1, T2)
{
    std::cout << "f1(T1, T2)\n";
}

template<typename T1, typename T2>
void f1(T2, T1)
{
    std::cout << "f1(T2, T1)\n";
}
// 到这里为止一切都是正确的

int main()
{
    f1<char, char>('a', 'b'); // 错误:二义性
}

在上面的代码中,虽然函数f1<T1 = char, T2 = char>(T1,T2)可以和函数f1<T1 = char, T2 =char>(T2, T1)同时存在,但是重载解析规则将不知道应该选择哪一个函数。

 

12.2.2 重载的函数模板的局部排序

template<typename T>
int f(T)
{
    return 1;
}

template<typename T>
int f(T*)
{
    return 2;
}

int main()
{
    std::cout << f(0) << std::endl;
    std::cout << f((int*)0) << std::endl;
}

让我们先考虑调用(f(0)):实参的类型是int,如果用int替换T,就能和第1个模板的参数匹配。然而,第2个模板的参数类型总是一个指针;因此,经过演绎之后,只有产生自第1个模板的实例才是该调用的候选函数。在这个调用中,重载解析并没有发挥作用。

第2个调用((f ( (int*) 0) )就显得比较有趣:对于这两个模板,实参演绎都可以获得成功,于是就获得两个函数,即f<int*>(int*)和f<int>(int*)。如果根据原来的重载解析观点,这两个函数和实参类型为 int*的调用的匹配程度是一样的,这也就意味着该调用是二义性的(见附录B)。然而,在这种情况下,还应该考虑重载解析的额外规则:选择“产生自更特殊的模板的函数”。因此(我们将在后面的小节看到),第2个模板被认为是更加特殊的模板,从而(再次)产生下面的输出结果:

1 
2


12.2.3 正式的排序原则

接下来,我们将给出一个精确的过程,它能够判断:在参与重载集的所有函数模板中,某个函数模板是否比另一个函数模板更加特殊。然而,我们应该知道这只是不完整的排序原则:就是说,两个模板也可能会被认为具有相同的特殊程度。如果重载解析必须在这两个特殊程度相同的模板中进行选择,那么将不能做出任何决定,也就是说程序包含了一个二义性错误。

假设我们要比较两个同名的函数模板ft1和ft2,对于给定的函数调用,它们看起来都是可行的。在我们下面的讨论中,对于没有被使用的缺省函数实参和省略号参数,我们将不考虑。接下来,通过如下替换模板参数,我们将为这两个模板虚构两份不同的实参类型(如果是转型函数模板,那么还包括返回类型)列表,其中第1份列表针对第1个模板,第2份列表针对第2个模板。“虚构”的实参列表将这样地替换每个模板参数:

1.用唯一的“虚构”类型替换每个模板类型参数。

2.用唯一的“虚构”类模板替换每个模板的模板参数。

3.用唯一的适当类型的“虚构”值替换每个非类型参数。

如果第 2 个模板针对第 1 份列表可以进行成功的实参演绎(能够进行精确的匹配),而第1个模板针对第2份列表的实参演绎以失败告终,那么我们就称第1个模板要比第2个模板更加特殊。反之,如果第1个模板针对第2份列表可以进行成功的实参演绎(能够进行精确的匹配),而第2个模板针对第1份列表的实参演绎失败,那么我们就称第2个模板要比第1个模板更加特殊。否则的话(或者是两个都不能成功演绎,或者是两个都能成功演绎),我们就称这两个模板之间不存在特殊的排序关系。

让我们把这个过程应用于前面的例子,来更加清楚地阐明上面的问题。根据这两个模板和前面所描述的模板参数替换方法,我们虚构了两个实参类型列表:(A1)和(A2*)(A1和A2是不同的虚构类型)。显然,第1个模板可以成功地演绎第2份实参列表,只要用A2* 替换T就可以。然而,第2个模板却不能成功地演绎第1份列表,因为第2个模板的T*是不能和非指针类型A1进行匹配的。因此,我们就可以(正式地)得出结论:第2个模板比第1个模板更加特殊。

 

12.2.4 模板和非模板

函数模板也可以和非模板函数同时重载。当其它的所有条件都是一样的时候,实际的函数调用将会优先选择非模板函数。

 

12.3 显式特化

具有对函数模板进行重载的这种能力,再加上可以利用局部排序规则选择最佳匹配的函数模板,我们就能够给泛型实现添加更加特殊的模板,从而可以透明地获得具有更高效率的代码。然而,类模板是不能被重载的;但我们可以选择另一种替换的机制来实现这种透明自定义类模板的能力,那就是显式特化。

 

12.3.1 全局的类模板特化

引入全局特化需要用到下面3个标记序列:template、< 和 > 。另外,紧跟在类名称声明后面的就是要进行特化的模板实参。下面的例子说明了这一点:

template<typename T>
class S 
{
public:
    void info() 
         {
            std::cout << "generic (S<T>::info())\n";
         }
};

template<>
class S<void> 
{
public:
    void msg() 
         {
            std::cout << "fully specialized (S<void>::msg())\n";
         }
}

我们看到,全局特化的实现并不需要与(原来的)泛型实现有任何关联,这就允许我们可以包含不同名称的成员函数(info相对msg)。实际上,全局特化只和类模板的名称有关联。

另外,指定的模板实参列表必须和相应的模板参数列表一一对应。例如,我们不能用一个非类型值来替换一个模板类型参数。然而,如果模板参数具有缺省模板实参,那么用来替换的模板实参就是可选的(即不是必须的):

template<typename T>
class Types 
{
public:
    typedef int I;
};

template<typename T, typename U = typename Types<T>::I>
class S;           // (1)

template<> class S<void>  // (2)
{       
public:
    void f();
};
template<> class S<char, char>; // (3)
template<> class S<char, 0>;  // 错误:不能用0来替换U

int main()
{
    S<int>*   pi;  // 正确:使用(1),这里不需要定义
    S<int>   e1;  // 错误:使用(1),需要定义,但找不到定义
    S<void>*  pv;  // 正确:使用(2)
    S<void,int> sv;  // 正确:使用(2),这里定义是存在的
    S<void,char> e2;  // 错误:使用(1),需要定义,但找不到定义
    S<char,char> e3;  // 错误:使用(3),需要定义,但找不到定义
}

template<>
class S<char, char> // (3)处的定义
{};

如例子中所示,(模板)全局特化的声明并不一定是定义。另外,当一个全局特化声明之后,针对该(特化的)模板实参列表的调用,将不再使用模板的泛型定义,而是使用这个全局特化的定义。因此,如果在调用处需要该特化的定义,而在这之前并没有提供这个定义,那么程序将会出现错误。对于类模板特化而言,“前置声明”类型有时候是很有用的,因为这样就可以构造相互依赖的类型。另外,以这种方式获得的全局特化声明(应该记住它并不是模板声明)和普通的类声明是类似的,唯一的区别在于语法以及该特化的声明必须匹配前面的模板声明。

对于特化声明而言,因为它并不是模板声明,所以应该使用(位于类外部)的普通成员定义语法,来定义全局类模板特化的成员(也就是说,不能指定template<>前缀):

template<typename T>
class S;

template<> class S<char**> 
{
public:
    void print() const;
};

//下面的定义不能使用template<>前缀
void S<char**>::print() const
{
    std::cout << "pointer to pointer to char\n";
}

我们可以使用一个更复杂的例子来进一步理解这个概念:

template<typename T>
class Outside 
{
public:
    template<typename U>
    class Inside {
    };
};

template<>
class Outside<void> 
{
// 下面的嵌套类和前面定义的泛型模板之间并不存在联系
    template<typename U>
    class Inside
    {
    private:
        static int count;
    };
};

//下面的定义不能使用template<>前缀
template<typename U>
int Outside<void>::Inside<U>::count = 1;

可以用全局模板特化来代替对应泛型模板的某个实例化体。然而,全局模板特化和由模板生成的实例化版本是不能够共存于同一个程序中的。如果试图在同一个文件中使用这两者的话,那么通常都会导致一个编译期错误

template <typename T>
class Invalid {
};

Invalid<double> x1;  // 产生一个Invalid<double>实例化体

template<>
class Invalid<double>; // 错误:Invalid<double>已经被实例化了

 

12.3.2 全局的函数模板特化

就语法及其后所蕴涵的原则而言,(显式的)全局函数模板特化和类模板特化大体上是一致的,唯一的区别在于:函数模板特化引入了重载和实参演绎这两个概念。

如果可以借助实参演绎(用实参类型来演绎声明中给出的参数类型)来确定模板的特殊化版本,那么全局特化就可以不声明显式的模板实参。让我们考虑下面的例子:

template<typename T>
int f(T)       // (1)
{
    return 1;
}

template<typename T>
int f(T*)       // (2)
{
    return 2;
}

template<> int f(int)  // OK: (1)的特化
{
    return 3;
}

template<> int f(int*) // OK: (2)的特化。
{
    return 4;
}

全局函数模板特化不能包含缺省的实参值。然而,对于基本(即要被特化的)模板所指定的任何缺省实参,显式特化版本都可以应用这些缺省实参值。例如:

template<typename T>
int f(T, T x = 42)
{
    return x;
}

template<> int f(int, int = 35) // 错误,不能包含缺省实参值
{
    return 0;
}

template<typename T>
int g(T, T x = 42)
{
    return x;
}

template<> int g(int, int y)
{
    return y/2;
}

int main()
{
    std::cout << g(0) << std::endl; // 正确,输出21
}

 

12.3.3 全局成员特化

除了成员模板之外,类模板的成员函数和普通的静态成员变量也可以被全局特化;实现特化的语法会要求给每个外围类模板加上template<>前缀。如果要对一个成员模板进行特化,也必须加上另一个template<>前缀,来说明该声明表示的是一个特化。为了说明这些含义,让我们假设具有下面的声明:

template<typename T>
class Outer 
{             // (1)
public:
    template<typename U>
    class Inner 
    {       // (2)
    private:
        static int count;       // (3)
    };

    static int code;        // (4)

    void print() const           // (5)
    {        
        std::cout << "generic";
    }
};

template<typename T>
int Outer<T>::code = 6;        // (6)

template<typename T> 
template<typename U>
int Outer<T>::Inner<U>::count = 7;  // (7)

template<> class Outer<bool> 
{          // (8)
public:
    template<typename U>
    class Inner 
    {           // (9)
    private:
        static int count;       // (10)
    };

void print() const {        // (11)
}
};

在(1)处的泛型模板Outer中,(4)处的code和(5)处print(),这两个普通成员都具有一个外围类模板。因此,需要使用一个template<>前缀说明:后面将用一个模板实参集来对它进行全局特化:

template<>
int Outer<void>::code = 12;

template<>
void Outer<void>::print() const
{
    std::cout << "Outer<void>";
}

这些定义将会用于替代类Outer<void>在(4)处和(5)处的泛型定义;但是,类Outer<void>的其它成员仍然默认地产生自(1)处的模板。另外,在提供了上面的声明之后,就不能再次提供Outer<void>的显式特化。

类似于全局函数模板特化,我们需要一种可以在不指定定义的前提下(为了避免多处定义),可以声明类模板普通成员特化的方法。尽管对于普通类的成员函数和静态成员变量而言,非定义的类外声明在C++中是不允许的;但如果是针对类模板的特化成员,该声明则是合法的。也就是说,前面的定义可以具有如下声明:

template<>
int Outer<void>::code;
template<>
void Outer<void>::print() const;

对于成员模板Outer<T>::Inner,也可以用一个特定的模板实参对它进行特化,而且对于该特化所在的外围 Outer<T>而言,这个特化操作并不会影响 Outer<T>相应实例化体的其它成员。另外,由于存在一个外围模板(也就是Outer<T>),所以我们需要添加一个template<>前缀。最后所获得的代码大致如下:

template<> template<typename X>
class Outer<wchar_t>::Inner 
{
public:
    static long count; // 成员类型发生了改变
};

template<> template<typename X>
long Outer<wchar_t>::Inner<X>::count;

模板Outer<T>::Inner也可以被全局特化,但只能针对Outer<T>的某个给定实例。而且,我们需要添加两个 template<>前缀:因为外围类需要一个 template<>前缀,我们所要全局特化的内围模板(inner template)也需要一个template<>前缀:

template<>
template<>
class Outer<char>::Inner<wchar_t> 
{
public:
    enum { count = 1};
};

// 下面的C++程序是不合法的:
// template<> 不能位于模板实参列表的后面
template<typename X>
template<> class Outer<X>::Inner<void>; // 错误

我们可以将上面这个特化与Outer<bool>的成员模板的特化比较一下。由于Outer<bool>已经在前面全局特化了,所有它的成员模板也就不存在外围模板,因此我们就只需要一个template<>前缀:

template<>
class Outer<bool>::Inner<wchar_t> 
{
public:
    enum { count = 2 };
};

 

12.4 局部的类模板特化

全局模板特化通常都是很有用的, 但有时候我们更希望把类模板特化成一个“针对模板实参”的类家族, 而不是针对“一个具体实参列表”的全局特化。 例如, 假设下面是一个实现链表功能的类模板:

template<typename T>
class List { // (1)
public:
	… 
	void append(T const&);
	inline size_t length() const;
	…
};

对于某个使用这个模板的大项目, 它可能会基于多种类型来实例化该模板的成员。 于是, 对于那些没有进行内联扩展的成员函数(譬如List<T>::append()) , 这就可能会明显增加目标代码的大小。 然而, 如果我们从一个更低层次的实现来看, List<int*>::append()的代码和List<void*>::append()的代码是完全相同的。 也就是说, 我们希望可以让所有的指针List共享同一个实现。 尽管我们不能直接用C++来表达这种实现, 但我们可以指定所有的指针List都实例化自一个不同的模板定义, 从而近似地获得这种实现:

template<typename T>
class List < T* > 
{ // (2)
private:
	List<void*> impl;
	…
public:
	… 
	void append(T* p) 
	{
		impl.append(p);
	} 
	size_t length() const 
	{
		return impl.length();
	} …
};

在这种情况下, 我们把原来的模板(即(1) 处的模板) 称为基本模板, 而后一个定义则被称为局部特化(因为该模板定义所使用的模板实参只是被局部指定) 。 表示一个局部特化的语法包括: 一个模板参数列表声明(template<…>) 和在类模板名称后面显式指定的模板实参列表(在我们的例子中是<T*>) 。

我们前面的代码还存在一个问题, 因为 List<void*>会递归地包含一个相同类型的List<void*>成员。 为了打破这种无限递归, 我们可以在这个局部特化前面先提供一个全局特化:

template<>
class List<void*> 
{ // (3)
    … 
    void append (void* p);
    inline size_t length() const;
    …
};

这样, 一切才是正确的。 因为当进行匹配的时候, 全局特化会优于局部特化。 于是, 指针List的所有成员函数都被委托给List<void*>的实现(通过容易内联的函数) 。 针对C++模板备受指责的代码膨胀的缺点, 这也是克服该缺点的有效方法之一。

对于局部特化声明的参数列表和实参列表, 存在一些约束。 下面就是一些重要的约束:

1.局部特化的实参必须和基本模板的相应参数在种类上(可以是类型、 非类型或者模板) 是匹配的。

2.局部特化的参数列表不能具有缺省实参; 但局部特化仍然可以使用基本类模板的缺省实参。

3.局部特化的非类型实参只能是非类型值, 或者是普通的非类型模板参数; 而不能是更复杂的依赖型表达式(诸如2*N, 其中N是模板参数) 。

4.局部特化的模板实参列表不能和基本模板的参数列表完全等同(不考虑重新命名) 。

下面的例子详细地说明了这些约束:

template<typename T, int I = 3>
class S; // 基本模板

template<typename T>
class S < int, T > ; // 错误: 参数类型不匹配

template<typename T = int>
class S < T, 10 > ; // 错误: 不能具有缺省实参

template<int I>
class S < int, I * 2 > ; // 错误: 不能有非类型的表达式

template<typename U, int K>
class S < U, K > ; // 错误: 局部特化和基本模板之间没有本质的区别

每个局部特化(和每个全局特化一样) 都会和基本模板发生关联。当使用一个模板的时候, 编译器肯定会对基本模板进行查找, 但接下来会匹配调用实参和相关特化的实参, 然后确定应该选择哪一个模板实现。 如果能够找到多个匹配的特化, 那么将会选择“最特殊”的特化(和重载函数模板所定义的原则一样) ; 如果有未能找到“最特殊”的一个特化, 即存在几个特殊程度一样的特化, 那么程序将会包含一个二义性错误。

最后, 我们应该指出: 类模板局部特化的参数个数是可以和基本模板不一样的; 既可以比基本模板多, 也可以比基本模板少。 让我们再次考虑泛型模板List(在(1) 处声明) 。 我们已经讨论了应该如何优化指针List的实现, 但我们希望可以针对(特定的) 成员指针类型实现这种优化。 下面的代码就是针对指向成员指针的指针(pointer-to-memberpointers) , 来实现这种优化:

template<typename C>
class List < void* C::* >
{ // (4)
public:
	// 针对指向void*的成员指针的特化
	// 除了void*类型之外, 每个指向成员指针的指针类型都会使用这个特化
	typedef void* C::*ElementType;
	… 
	void append(ElementType pm);
	inline size_t length() const;
	…
};

template<typename T, typename C>
class List < T* C::* > { // (5)
private:
	List<void* C::*> impl;
	…
public:
	// 针对任何指向成员指针的指针类型的局部特化
	// 除了指向void*的成员指针类型, 它在前面已经处理了
	// 我们看到这个局部特化具有两个模板参数
	// 然而基本模板却只有一个参数
	typedef T* C::*ElementType;
	… 
	void append(ElementType pm) 
	{
		impl.append((void* C::*)pm);
	} 
	inline size_t length() const 
	{
		return impl.length();
	} 
	…
};

除了模板参数数量不同之外, 我们看到在(4) 处定义的公共实现本身也是一个局部特化(对于简单的指针例子, 这里应该是一个全局特化) , 而所有其它的局部特化((5) 处的声明) 都是把实现委托给这个公共实现。 显然, 在(4) 处的公共实现要比(5) 处的实现更加特殊化, 因此也就不会出现二义性问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[C++ Template]深入模板--特化与重载 的相关文章

  • Linux下编译CEF源码及交叉编译

    Linux下编译CEF chromium源码及交叉编译 官方编译文档 https bitbucket org chromiumembedded cef wiki MasterBuildQuickStart markdown header l
  • 继承 c++

    1 类的继承概念的解释 2 函数隐藏 3 赋值兼容 4 多种继承方式 5 函数的使用 构造函数 析构函数 拷贝构造函数 赋值运算符重载函数 1 类的继承概念解释 假定有一个类A 要创建一个新类B 它是类A的一个特殊版本 类A就称为基类 类B
  • java前台请求quartz,spring整合java quartz实现动态定时任务的前台网页配置与管理

    实例简介 在实际项目应用中经常会用到定时任务 可以通过quartz和spring的简单配置即可完成 但如果要改变任务的执行时间 频率 废弃任务等就需要改变配置甚至代码需要重启服务器 这里介绍一下如何通过quartz与spring的组合实现动
  • 目标主机SSH服务存在RC4、CBC或None弱加密算法 修复方法

    近期进行服务器漏扫时发现了 目标主机SSH服务存在RC4 CBC或None弱加密算法 的漏洞 记录一下修复方法 如下 1 修改 ssh 配置文件 vim etc ssh sshd config 或 vi etc ssh sshd confi
  • java打印出1~100之内的所有素数

    素数 质数 是指在大于1的整数之中只能被1和它自身整除的数就称之为素数 例如 2 1 2 2 2 1 2就是一个素数 以此类推3 5 7都是素数 代码 public class Main public static void main St
  • 主板电源开关接口图解_组装电脑时主板跳线如何接?DIY装机主板接线教程

    如今装机不再像以前那么神秘 不用再去电脑城问东问西 只要上天猫或京东等网上商城即可放心买到各种电脑配件 那么 自己组装电脑最难的是什么 CPU 散热器 内存 显卡安装都很简单 很多小伙伴自己组装电脑的难点主要在于主板跳线或者说机箱接线 今天
  • 2022.06.26 华为od机试真题

    华为od机试真题 1 最长连续子串 2 正方形数量 3 二叉树层次遍历 不会做 1 最长连续子串 有N个正整数组成的一个序列 给定一个整数sum 求长度最长的的连续子序列使他们的和等于sum 返回次子序列的长度 如果没有满足要求的序列 返回
  • 2019年广东工业智能机器人产量约占全国29%

    日前发布的 广东省制造业高质量发展 十四五 规划 下称 规划 中 智能机器人是我省 十四五 谋划发展的十大战略性新兴产业之一 战略性新兴产业是科技创新和产业发展的深度融合 规划针对智能机器人的发展提出 重点发展机器人减速器 控制器等关键部件
  • 【计算机视觉

    文章目录 一 PROMISE12 二 BraTS 2015 三 LIP Look into Person 四 BigEarthNet 五 Stanford Background Standford Background Dataset 六
  • win10 powershell无法激活conda v4.9环境

    1 PATH环境变量 把condabin目录添加到环境变量中 2 初始化powershell 2 1 管理员身份运行powershell win x 弹出选项 选中 Windows PowerShell 管理员 2 2 conda init
  • python 替换_Python 实现将numpy中的nan和inf,nan替换成对应的均值

    nan not a number inf infinity 正无穷 numpy中的nan和inf都是float类型 t t 返回bool类型的数组 矩阵 np count nonzero 返回的是数组中的非0元素个数 true的个数 np
  • (python)Hex文件解析和校验

    目录 前言 Hex文件结构分析 1 利用notepad 打开hex文件 2 hex行格式 行开始 数据长度 地址 数据类型 数据 校验和 3 校验和 完整代码 总结 前言 Intel HEX文件是由一行行符合Intel HEX文件格式的文本
  • 数据赋能企服增长,构建“以客户为中心”的数字化经营体系

    目前 中国企服市场数字化需求爆发 主要表现在传统行业数字化转型加速 企服企业的服务能力得到认可 新冠疫情加速数字化进程等方面 神策数据作为大数据分析与营销科技优质服务商 结合自身数字化实践经验 总结出企服数据驱动增长的解决方案 从市场营销
  • mysql查询like多个值

    有个需求是要查询字段中code前缀是H M 81 82开头的 方法一 使用like和or select from zhy where code like H or code like M or code like 81 or code li
  • Java并发包中那些值得学习的并发工具类(空谈误国,实干兴邦,代码示范,抛砖引玉)

    首先我们通常说的并发包就是java util concurrent包及其子包 集中了Java并发的各种基础工具类 一 这个并发包在哪 上面的包就是传说中的并发包 为什么这个并发包就比较流弊呢 原因主要有以下几点 提供了几个比synchron
  • 什么叫临界资源和临界区?

    临界资源是指每次仅允许一个进程访问的资源 属于临界资源的硬件有打印机 磁带机等 软件有消息缓冲队列 变量 数组 缓冲区等 诸进程间应采取互斥方式 实现对这种资源的共享 每个进程中访问临界资源的那段代码称为临界区 显然 若能保证诸进程互斥地进
  • 机器学习源代码_机器学习中程序源代码的静态分析

    机器学习源代码 Machine learning has firmly entrenched in a variety of human fields from speech recognition to medical diagnosin

随机推荐

  • Qt自定义委托

    Qt中的委托通常都是继承自QStyledItemDelegate或者QItemDelegate 二者的区别主要在于绘制方式 QStyledItemDelegate会使用当前样式绘制 并且能够使用qss 因此在在自定义委托时 一般使用 QSt
  • 详解Python视频剪辑第三方库Moviepy的使用

    一 简介 Moviepy是一个用于处理视频的Python第三方库 它提供了简单易用的接口 可以进行视频剪辑 合并 转码以及添加各种效果等操作 本文将介绍如何使用Moviepy库来进行视频剪辑的基本操作 二 安装Moviepy 在开始之前 我
  • 【状态估计】基于FOMIAUKF、分数阶模块、模型估计、多新息系数的电池SOC估计研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 数据 讲解 1 概述 准确估计电池的荷电状态 SOC 是
  • 前端Js框架汇总

    前端Js框架汇总 概述 有些日子没有正襟危坐写博客了 互联网飞速发展的时代 技术更新迭代的速度也在加快 看着Java Js Swift在各领域心花路放 也是煞是羡慕 寻了寻 net的消息 也是振奋人心 net core 1 mono xam
  • 通过机器学习识别鸟类_将机器学习带到鸟类

    通过机器学习识别鸟类 Cacophony Project的广阔愿景是使用最新技术来监视新西兰鸟类的数量 并人道地消除危害其的捕食者 该项目始于我们创始人的后院 以评估他保护鸟类财产的努力的有效性 从这个简单的开始 该项目便Swift发展成为
  • MySQL存储引擎MyISAM和InnoDB底层索引结构

    目录 一 存储引擎作用于什么对象 二 MyISAM和InnoDB对索引和数据的存储在磁盘上是如何体现的 三 MyISAM主键索引与辅助索引的结构 1 主键索引 2 辅助 非主键 索引 四 InnoDB主键索引与辅助索引的结构 1 主键索引
  • struts-config.xml的dtd文件的声明方式

    struts config xml 的 dtd 文件的声明方式 第一种 第二种 第一种是 PU
  • 【C++】stack容器

    1 stack基本概念 英st k 美st k n 整齐的 一堆 lt 英 gt 垛 堆 大量 许多 尤指工厂的 大烟囱 图书馆的 藏书架 双面书架 the stacks 定高分层盘旋 等待降落 的机群 计算机 存储 栈 竖着置放的高保真音
  • Android Service两种启动方式详解(总结版)

    1 概念 开始 先稍稍讲一点android中Service的概念和用途吧 Service分为本地服务 LocalService 和远程服务 RemoteService 1 本地服务依附在主进程上而不是独立的进程 这样在一定程度上节约了资源
  • Apache Dril l和 Prestodb是合并多租数据源户统一查询

    通常项目进行多租户管理后 领导又需要有某个用户可以查询所有多租户的信息 为了方便管理和统计分析 因此Apache Drill和 Prestodb 合并多租数据源户统一查询 Apache Drill Apache Drill是一个低延迟的分布
  • 分类算法:Logistic 回归(二分类,多分类)

    关于分类问题 结果常有两个 用 0 1 表示 0表示负类 常表示没有某样东西 1表示正类 表示具有我们要寻找的东西 将线性回归应用到分类问题 运气好的话 可能也会运行的很好 但这取决于数据集 所以将线性回归应用到分类问题并不是一个好的方法
  • 起泡法排序(十个数)

    include
  • 【自然语言处理】BOW和TF-IDF详解

    BOW 和 TF IDF 详解 机器无法处理原始形式的文本数据 我们需要将文本分解成一种易于机器阅读的数字格式 自然语言处理背后的理念 BOW 和 TF IDF 都是帮助我们将文本句子转换为向量的技术 我将用一个流行的例子来解释本文中的 B
  • YUV / RGB 格式及快速转换算法

    RGB TO YUV转换原理及代码示例 转 RGB TO YUV转换原理及代码示例 RGB TO YUV转换原理及代码示例 由于H 264等压缩算法都是在YUV的颜色空间上进行的 所有在进行压缩前 首先要进行颜色空间的转换 如果摄像头采集的
  • 一文弄懂Netty基本架构

    文章目录 Netty简介 Netty结构 Netty线程模型 服务端线程模型 客户端线程模型 NioEventLoop Task Netty中的Channel 工作原理 线程 ChannelPipeline和ChannelHandler C
  • flask连接mysql数据库操作

    Y21 以简单的年级表格模型为例 介绍数据库的增删改查 条件查询等操作 代码 以下 from app import db 定义数据模型 设置表格中各个字段的数据类型 class Grade db Model g id db Column d
  • Linux系统ftp服务设置

    目录 1 FTP服务介绍 1 1 FTP的优缺点 1 2 FTP用户的类型 1 3 常见的FTP服务器程序 1 4 常见的FTP客户端程序 ftp命令 2 vsftpd服务基础 2 1 vsftpd软件包 vsftpd 3 0 2 22 e
  • WebGL three.js学习笔记 创建three.js代码的基本框架

    WebGL学习 Three js学习笔记 1 webgl介绍 WebGL是一种3D绘图协议 它把JavaScript和OpenGL ES 2 0结合在一起 通过增加OpenGL ES 2 0的一个JavaScript绑定 WebGL可以为H
  • CryptoPP使用介绍

    Crypto 是个免费的C 加解密类库 由于资格太老 持续更新 最新版本到了CryptoPP 5 6 对天缘而言 第一眼看到CryptoPP就感觉头大 根目录下放置大量单源文件 编译文件 项目文件 再加上多平台和多编译器支持 文件几乎又多了
  • [C++ Template]深入模板--特化与重载

    目录 第12章 特化与重载 12 2 重载函数模板 12 2 1 签名 12 2 2 重载的函数模板的局部排序 12 2 3 正式的排序原则 12 2 4 模板和非模板 12 3 显式特化 12 3 1 全局的类模板特化 12 3 2 全局