我有两个函数
template <class T> void foo(std::vector<T>* p)
和
template <class T> void foo(std::valarray<T>* p)
函数体中的代码是相同的。有没有一种巧妙的方法来编写模板,这样我就可以避免重复?类似的东西
template <class T, class Container> void foo(Container<T>* p)
我正在使用C++14。
我只希望允许Container
为std::vector
或std::valarray
。
您也可以只为您的案例创建类型特征。
template <typename T>
struct is_vector_or_valarray : std::false_type {};
template<class T>
struct is_vector_or_valarray<std::vector<T>> : std::true_type {};
template<class T>
struct is_vector_or_valarray<std::valarray<T>> : std::true_type {};
template<typename C, typename = std::enable_if_t<is_vector_or_valarray<C>::value>>
void foo(C* p) {}
演示
如果您希望允许传入从std::vector
和std::valarray
派生的类。
template<template<typename...> typename T, typename U>
struct is_tbase_of
{
private:
template<class V>
static std::pair<V, decltype(static_cast<const T<V>&>(std::declval<U>()), std::true_type{})> test(const T<V>&);
static std::pair<U, std::false_type> test(...);
public:
using _aux_type = decltype(test(std::declval<U>()));
using base_type = T<typename _aux_type::first_type>;
static constexpr bool value = _aux_type::second_type::value;
};
template<typename C,
typename = std::enable_if_t<
is_tbase_of<std::vector, C>::value
|| is_tbase_of<std::valarray, C>::value>>
void foo(C* p) {
using base_t = std::conditional_t<
is_tbase_of<std::vector, C>::value,
typename is_tbase_of<std::vector, C>::base_type,
typename is_tbase_of<std::valarray, C>::base_type>;
auto pbase = static_cast<base_t*>(p);
// operate on pbase
}
用于检查类是否从模板类继承的类型特征来自这个答案。我在此基础上做了一些细微的更改(主要是为了获得基本类型)。
正如@Raymond所说,c
应该被强制转换为其基指针,以便对切片部分进行操作。我认为它应该在函数体中完成,否则,会有一个非推导的上下文。
演示
注意:你不能真正专门研究std::vector<>
和std::valarray<>
,但你可以说,对于给定的(推导的)T,你期望std::vector<T>
或std::valarray<T>
。因此,别名也将被解析。
诀窍是,为模板类C
和类型T
(以及可能的其他参数,如std
中的参数)临时化,然后使用仅在std::vector<T>
或std::valarray<T>
解析时解析(在这种情况下为空)的std::enable_if<>
。
另外请注意,您可能希望允许自定义分配器等,也就是说,建议对容器使用typename... Ts
,而不是单个T。
#include <iostream>
#include <vector>
#include <valarray>
#include <type_traits>
template<template<class...> class C, typename T>
auto foo(C<T>* arg) -> std::enable_if_t<std::is_same<C<T>, std::vector<T>>::value || std::is_same<C<T>, std::valarray<T>>::value>
{
}
int main() {
std::vector<int> v;
std::valarray<int> va;
//std::pair<int, int> p; // this should fail
foo(&v);
foo(&va);
//foo(&p); // this fails as expected
return 0;
}
如果您还想处理派生类,可以使用std::is_base_of<>
,但对于std
命名空间中的类,不建议从它们派生。