模板约束C ++
在C#中,我们可以定义一个通用types,对可以用作generics参数的types施加约束。 以下示例说明了通用约束的用法:
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 ++。
正如其他人所提到的,C ++ 0x正在把这个内置到语言中。 在此之前,我build议Bjarne Stroustrup 对模板约束的build议 。
编辑: 提升也有其自己的替代 。
Edit2:看起来像概念已经从C ++ 0x中删除 。
“含蓄”是正确的答案。 由于模板的编译方式,模板有效地创build了“鸭子打字”的场景。 你可以使用模板types的值来调用你想要的任何函数,唯一可以接受的实例就是定义该方法的那些实例。 例如:
template <class T> int compute_length(T *value) { return value->length(); }
我们可以在一个指向任何声明length()
方法返回一个int
types的指针上调用这个方法。 正是如此:
string s = "test"; vector<int> vec; int i = 0; compute_length(&s); compute_length(&vec);
…而不是指向不声明length()
的types的指针:
compute_length(&i);
这第三个例子不会编译。
这是有效的,因为C ++为每个实例化编译了模板化函数(或类)的新版本。 执行该编译时,在进行types检查之前,它将模板实例化直接,近似macros观地replace为代码。 如果所有东西都仍然适用于这个模板,那么编译就会继续,最终我们会得出结果。 如果任何失败(如int*
不声明length()
),那么我们得到了可怕的六页模板编译时错误。
如果你使用C ++ 11,你可以使用static_assert
和std::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"); // ... } }
你可以在IFoo上放置一个不做任何事的警卫types,确保它在Foo中的T:
class IFoo { public: typedef int IsDerivedFromIFoo; }; template <typename T> class Foo<T> { typedef typename T::IsDerivedFromIFoo IFooGuard; }
检查提升
提升概念检查图书馆(BCCL)
Concept Check库允许添加显式语句并以提议的C ++语言扩展的风格检查概念 。
有点。 如果将static_cast传送给IFoo *,则不可能实例化模板,除非调用者传递了可分配给IFoo *的类。
只是隐含的。
您在实际调用的方法中使用的任何方法都强加在模板参数上。
你能行的。 创build基本模板。 使它只有私人构造函数。 然后为每个要允许的情况创build专门化(如果不允许的列表比允许的列表小得多,则做相反的处理)。
编译器将不允许您使用私有构造函数实例化使用该版本的模板。
这个例子只允许用int和float实例化。
template<class t> class FOO { private: FOO(){}}; template<> class FOO<int>{public: FOO(){}}; template<> class FOO<float>{public: FOO(){}};
它不是一个简短而优雅的做法,但它是可能的。
看看CRTP模式(好奇recursion模板模式)。 它旨在帮助支持静态的inheritance。