我有一个类似于模板的类:
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
我想添加一个只适用于给定类型T的成员函数。是否有可能做到这一点,而不必专门化类并重新实现所有其他已经存在的方法呢?
谢谢
我有一个类似于模板的类:
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
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 !
}
这利用了编译器只有在实际使用类模板的成员函数时才实例化类模板的成员函数(而不是在实例化类模板本身时)的事实。通过上述解决方案,当编译器尝试这样做时,由于执行static_assert
而失败。
C++标准参考:
§ 14.7.1 Implicit instantiation
[temp.inst]
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[ Example:
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 }
Nothing in this example requires
class Z<double>
,Z<int>::g()
, orZ<char>::f()
to be implicitly instantiated. — end example ]
static_assert
提供了一个很好的错误信息,但是也很好知道你可以专门为单个成员函数进行特化而不会出现代码重复。只需给通用的 onlyForInts()
函数提供一个空的 no-op 实现,并在类外部为 int
进行特化:http://coliru.stacked-crooked.com/a/2f790496fb1b3cf3 - TemplateRex使用C++03和CRTP(奇异递归模板模式)是可能的:Curiously recurring template pattern。
#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();
}
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;
}
static_assert
技术非常好用。但是,如果你能够在不重复代码的情况下专门为单个成员函数进行特化,那也是很好的。只需给通用的 onlyForInts()
函数提供一个空的 no-op 实现,并在类外专门为 int
进行特化即可。 < /p >
#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 !
}
实时示例。
如果您想在不完全禁用通用行为的情况下具有 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;
};
static_cast
更好,因为它不会进入函数内部(它不存在),并且在编译时会给出一个好的错误信息(GCC在我的机器上显示“has no member named ‘only_for_ints’”)。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
protected
而不是private
数据? - TemplateRexmy_vector
进行有意义的操作,可以使用此模板。 - RamblingMad