c++类模板专门化,无需重新实现所有内容
c++ class template specialization, without having to reimplement everything
我有一个模板化的类,像这样:
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
我想添加一个成员函数,只适用于1种给定类型的T.是否有可能做到这一点,而不必专门化类并重新实现所有其他已经存在的方法?
谢谢
最简单、最干净的解决方案是在方法体中使用static_assert()
,拒绝除所选类型之外的其他类型(在下面的示例中只接受整数):
#include <type_traits>
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
static_assert(std::is_same<T, int>::value, "Works only with ints!");
}
protected:
std::vector<T> myVector;
};
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
//f.onlyForInts(3.14f); // does not compile !
}
OK CASE DEMONOK CASE DEMO
这利用了这样一个事实,即编译器只在实际使用时才实例化类模板的成员函数(而不是在类模板本身实例化时)。使用上述解决方案,当编译器尝试这样做时,由于执行static_assert
而失败。
§14.7.1隐式实例化
[temp.inst]
除非函数模板专门化已被显式实例化或显式特化,否则函数模板专门化在需要存在函数定义的上下文中被引用时将被隐式实例化。除非调用函数模板显式特化或显式特化类模板的成员函数,否则在需要默认实参值的上下文中调用函数模板或类模板的成员函数时,将隐式实例化函数模板的默认实参或成员函数。
(例子:
template<class T> struct Z { void f(); void g(); }; void h() { Z<int> a; // instantiation of class Z<int> required Z<char>* p; // instantiation of class Z<char> not required Z<double>* q; // instantiation of class Z<double> not required a.f(); // instantiation of Z<int>::f() required p->g(); // instantiation of class Z<char> required, and // instantiation of Z<char>::g() required }
本例中不要求隐式地设置
class Z<double>
、Z<int>::g()
或Z<char>::f()
实例化。- 结束示例]
是的,在c++ 03中使用CRTP(奇怪的循环模板模式)是可能的:
#include <numeric>
#include <vector>
template<typename Derived, typename T>
struct Base
{
};
template<typename Derived>
struct Base<Derived, int>
{
int Sum() const
{
return std::accumulate(static_cast<Derived const*>(this)->myVector.begin(), static_cast<Derived const*>(this)->myVector.end(), int());
}
};
template<typename T>
class A : public Base<A<T>, T>
{
friend class Base<A<T>, T>;
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
};
int main()
{
A<int> Foo;
Foo.Sum();
}
作为另一种解决方案,它也适用于普通c++ 03(与static_assert
或enable_if
解决方案相反),您可以添加额外的默认模板参数,从而使您同时拥有这两种解决方案类的特化和非特化版本。然后,您可以从非专门化版本继承专门化版本。
#include <vector>
template<typename T, bool unspecialized = false>
class A
{
protected:
std::vector<T> myVector;
public:
void setVec(const std::vector<T>& vec) { myVector = vec; }
/*
constructors + a bunch of member functions here
*/
};
template<>
class A<int, false> : public A<int, true>
{
public:
int onlyForInt() {
return 25;
}
};
int main() {
// your code goes here
std::vector<int> vec;
A<int> a;
a.setVec(vec);
a.onlyForInt();
return 0;
}
这个解决方案的缺点是需要添加构造函数转发器,if类具有非平凡构造函数。
@PiotrS的static_assert
技术。很好地工作。但是知道您可以在不重复代码的情况下专门化单个成员函数也很好。只要给通用的onlyForInts()
一个空的无操作实现,并对int
进行类外专门化
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
// no-op
}
protected:
std::vector<T> myVector;
};
template<>
void A<int>::onlyForInts(int t)
{
// works
}
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
f.onlyForInts(3.14f); // compiles, but does nothing !
}
<<p> 生活例子/strong>。如果您希望具有int
特定的行为而不完全禁用通用行为,则此技术会派上用场。
答案中尚未给出的一种方法是使用标准库std::enable_if
在继承到定义适当成员函数的主类的基类上执行SFINAE。
示例代码:
template<typename T, class Enable = void>
class A_base;
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>{
public:
void only_for_ints(){/* integer-based function */}
};
template<typename T>
class A_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>{
public:
// maybe specialize for non-int
};
template<typename T>
class A: public A_base<T>{
protected:
std::vector<T> my_vector;
};
这种方法将比一个空函数更好,因为你对你的API更严格,比static_cast
更好,因为它根本不会使它进入函数的内部(它将不存在),并且会在编译时给你一个漂亮的错误消息(GCC显示"没有名为' only_for_int '的成员"在我的机器上)。
这种方法的缺点是编译时间和代码膨胀,但我不认为它太沉重。
(你敢说c++ 11的要求是一个缺点,我们在2014年,该死的,下一个标准甚至已经完成了!)
另外,我注意到,您可能必须在基类而不是final类中定义my_vector
,因为您可能希望在成员函数中处理该数据。
一个不需要重复一堆代码的好方法是创建一个基类,并在基类中继承这个类。
的例子:
template<typename T>
class base_data{
protected:
std::vector<T> my_vector;
};
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>: public base_bata<T>{
public:
void only_for_ints(){/* phew, finally. fiddle around with my_vector! */}
};
// non-integer A-base
template<typename T>
class A: public A_base<T>{
protected:
// helper functions not available in base
};
这确实留下了一个看起来很可怕的多重继承方案,但它非常可行,并且使基于模板参数定义成员变得容易(为了将来的验证)。
人们通常不喜欢多重继承,也不喜欢SFINAE看起来多么复杂/混乱,但现在我知道了它,没有它我就活不下去了:静态代码的速度与动态代码的多态性!
不确定我在哪里找到这个,但是您可以使用= delete;
作为类内的函数定义,从而删除一般情况下的函数,然后在类外显式地专门化:
template <typename T>
struct A
{
auto int_only(T) -> void = delete;
};
template <> auto A<int>::int_only(int) -> void {}
int main()
{
auto a_int = A<int>{};
auto a_dbl = A<double>{};
a_int.int_only(0);
// a_dbl.int_only(3.14); error: call to deleted member function
}
https://en.cppreference.com/w/cpp/language/function Deleted_functions
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- C++ 如何使用动态计算的新节点实现 A*?
- 实现一个函数,该函数将字符串作为输入并返回一个新字符串,辅音字母不替换为 "!"
- 为什么这个新的 [ ] 和删除 [ ] 实现会分解为 12 >整数?
- 在不创建新节点的情况下实现带有映射的trie
- 使用 Qt5 的新信号/插槽实现向滑块发出信号
- 如何实现由TPAINTBOX创建的新组件的OnMousedown,OnMouseUp事件
- 为什么我不能使私人运营商成为新的并使用默认实现?
- 如何向 C++ 中无法访问其实现的类添加新函数
- 在哪里可以找到C 中新运营商的确切实现
- C++新的不同实现
- 如何实现一个创建新对象并返回对它的引用的C++方法
- 如何实现消费者生产者,消费者可以请求新的数据
- 如何实现C++"新"运算符
- 我可以在不使用新关键字的情况下以某种方式实现这一点吗?C++
- 如何实现一个简单的容器放置新和放置功能
- 您将如何重构这种多态设计,以便在添加新实现时使其更加灵活?
- 为什么不使用模板实现新内容?
- 如何实现内部实现依赖于模板参数的类
- 库函数的新实现,并在其中调用旧实现