c++ 概念和需求的例子

578 阅读1分钟

c++ 概念和需求的例子

请参考以下函数模板,该模板返回两个最大值:

template<typename T>

T maxValue(T a,Tb)

{ return b<a?a:b; }

对于两个类型相同的参数,可以调用此函数模板,前提是对参数执行的操作(与运算符比较<和复制)有效。但是,传递两个指针将比较它们的地址而不是它们引用的值。

逐步改进模板

为了解决这个问题,我们可以为模板配备一个约束,以便在传递原始指针时它不可用:

template<typename T>

requires (!std::is_pointer_v<T>) T maxValue(T a,T b)

{

return b<a?a:b;}

在这里,约束是在 require 子句中表述的,该子句与关键字 require 一起引入(还有其他方法可以表述约束)。

为了指定模板不能用于原始指针的约束,我们使用标准类型特征 std::is_pointer_v<>(它生成标准类型特征 std::is_pointer<> 的值成员)。

有了这个约束,我们不能再将函数模板用于原始指针:

int x=42; int y=77;

std::cout <<maxValue(x,y)<<'\n';

// OK: int 的最大值

std::cout <<maxValue(&x,&y)<<'\n';

//错误:约束不满足

该需求是编译时检查,对已编译代码的性能没有影响。这仅仅意味着模板不能用于原始指针。当传递原始指针时,编译器的行为就像模板不存在一样。

定义和使用一个概念

我们很可能不止一次地需要一个指针约束。因此,我们可以为它引入一个概念

template<typename T>

concept IsPointer = std::is_pointer_v<T>;

概念是一个模板,我们在其中为传递的模板参数的一个或多个约束引入了一个名称。

在等号之后,我们必须将约束指定为在编译时计算的布尔表达式。

在这种情况下,我们要求传递给 IsPointer 的 temnplate 参数<>必须是原始指针。

现在,我们可以用这个概念来约束 maxValue()模板:

template<typename T>

requires (!IsPointer<T>) T maxValue(T a,T b)

{

return b<a?a:b; }

概念重载

通过使用约束和概念,我们甚至可以重载maxValue()模板,让一个实现用于指针,另一个实现用于其他类型

template<typename T> 

requires (!IsPointer<T>) 

T maxValue(T a,Tb)

// maxValue() 不用于指针

{

return b<a?a:b;  //比较值

 }

template<typename T> 

requires IsPointer<T> 

auto maxValue(T a,T b)  //指针的 maxValue ()

{

return maxValue(*a,*b);  //比较指针指向的值

}

请注意,只需用概念约束模板的require子句不再需要圆括号。现在,我们有两个同名的函数模板,但每种类型只有一个可用:

int x=42; 

int y=77;

std::cout << maxValue(x,y) <<'\n';   //调用 maxValue() 不用于指针

std::cout<<maxValue(&x,&y)<<'\n'; //为指针调用 maxValue()


因为指针的实现将返回值的计算委托给指针所引用的对象,所以第二个调用使用了两个maxValue()模板。

当将指针传递给int时,我们实例化T为int*的指针模板,并实例化T为int时不用于指针的基本maxValue()模板。

这甚至可以递归地工作。我们可以要求指向int型指针的指针的最大值

int* xp=&x;

int* yp=&y;

std::cout <<maxValue(&xp,&yp)<<'\n'; //对于 int * * 使用 callsmaxValue ()

带概念的重载解决方案

重载解析认为有约束的模板比没有约束的模板更特殊。因此,只约束指针的实现就足够了

template<typename T> T maxValue(T a,T b)  //maxValue()表示 T 类型的值 

{

return b<a?a:b; //比较值

}

template<typename T> 

requires IsPointer<T> 

auto maxValue(T a,T b)  //maxValue()用于指针(更高的优先级)

}

return maxValue(*a,*b);  //指针指向的 compure 值 

}

但是,要小心:一次使用引用和一次不使用引用的重载可能会导致歧义。通过使用概念,甚至可以选择一些约束而不是其他约束。然而,这要求使用包含其他概念的概念。


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情