模板约束 C + +

在 C # 中,我们可以定义一个泛型类型,它对可以用作泛型参数的类型施加约束。下面的示例说明了通用约束的用法:

interface IFoo
{
}




class Foo<T> where T : IFoo
{
}


class Bar : IFoo
{
}


class Simpson
{
}


class Program
{
static void Main(string[] args)
{
Foo<Bar> a = new Foo<Bar>();
Foo<Simpson> b = new Foo<Simpson>(); // error CS0309
}
}

有没有一种方法可以在 C + + 中对模板参数施加约束。


C + + 0x 对此有本地支持,但我说的是当前的标准 C + + 。

59059 次浏览

只是暗示。
在实际调用的方法中使用的任何方法都强加在模板参数上。

看看 加油

Boost 概念检查库(BCCL)

概念检查库允许以 提出的 C + + 语言扩展的样式添加显式语句和检查 概念

算是吧。如果您将 static _ cast 转换为一个 IFoo * ,那么将不可能实例化该模板,除非调用方传递一个可以分配给 IFoo * 的类。

正如有人提到的,C + + 0x 正在把这个内置到语言中。

编辑: 加油也有一个 自己的选择

编辑2: 看起来像 概念已经从 C + + 0x 中删除

“含蓄”才是正确答案。由于模板的编译方式,它们有效地创建了一个“鸭式输入”场景。您可以对模板类型的值调用任何希望调用的函数,并且只有那些定义了该方法的实例才会被接受。例如:

template <class T>
int compute_length(T *value)
{
return value->length();
}

我们可以在指向声明 length()方法返回 int的任何类型的指针上调用此方法。因此:

string s = "test";
vector<int> vec;
int i = 0;


compute_length(&s);
compute_length(&vec);

... 但不是在指向 没有声明 length()的类型的指针上:

compute_length(&i);

第三个示例将不会编译。

这是因为 C + + 为每个实例化编译了一个新版本的模板化函数(或类)。在执行这种编译时,它会在类型检查之前将模板实例化直接替换到代码中,这种替换几乎与宏类似。如果所有内容仍然与该模板一起工作,那么编译将继续进行,最终得到一个结果。如果有任何错误(比如 int*没有声明 length()) ,那么我们就会得到可怕的六页模板编译时错误。

你可以在 IFoo 上设置一个什么都不做的保护类型,确保它在 Foo 的 T 上:

class IFoo
{
public:
typedef int IsDerivedFromIFoo;
};


template <typename T>
class Foo<T>
{
typedef typename T::IsDerivedFromIFoo IFooGuard;
}

你可以的。创建基本模板。使它只有私有构造函数。然后为每个您希望允许的情况创建专门化(或者,如果禁止列表比允许列表小得多,则创建相反的专门化)。

编译器将不允许您实例化使用私有构造函数的版本的模板。

此示例只允许使用 int 和 float 进行实例化。

template<class t> class FOO { private: FOO(){}};


template<> class FOO<int>{public: FOO(){}};


template<> class FOO<float>{public: FOO(){}};

这并不是一种简短而优雅的方式,而是一种可能的方式。

看看 CRTP 模式(古怪递归模板模式) ,它的设计目的是帮助支持静态继承。

如果使用 C + + 11,则可以将 static_assertstd::is_base_of一起用于此目的。

比如说,

#include <type_traits>


template<typename T>
class YourClass {


YourClass() {
// Compile-time check
static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");


// ...
}
}

使用 C + + 20,是的,有: 约束和概念

也许您希望保证模板是从特定类派生的:

#include <concepts>


template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;


class ABase { };
class ADerived : ABase { };


template<Derived<ABase> T>
class AClass {
T aMemberDerivedFromABase;
};

然后像正常情况一样编译以下内容:

int main () {
AClass<ADerived> aClass;
return 0;
}

但现在,当你做违反约束的事情:

class AnotherClass {


};
int main () {
AClass<AnotherClass> aClass;
return 0;
}

AnotherClass 不是从 ABase 派生出来的,因此我的编译器(GCC)给出了大致如下错误:

在函数‘ int main ()’中: 注意: 不满足约束 注意: 表达式‘ std: : is _ base _ of < U,T > : : value [ U = ABase; T = AnotherClass ]’被评估为‘ false’ 9 | 概念派生 = std: : is _ base _ of < U,T > : : value;

正如您可以想象的那样,这个特性非常有用,并且可以做的比约束一个类具有特定的基要多得多。