我有一个模板类和一个成员函数print()
来打印数据。
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
然后,我要打印标量数据或 vector 数据,因此我给出了专门的定义并出现了编译器错误。
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
问题:为什么该成员函数专门化会出错?为矢量定义打印功能的正确方法是什么?
解决方案1 :我已经测试了以下定义。
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
这个函数起作用了,但是我必须将其他成员函数复制到这个专门的类中。
编辑:
解决方案2 :为了防止复制所有其他成员函数,我定义了一个包含公共成员函数的基类,并从该基类继承:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
此解决方案效果很好。是否有一些更好或更常规的解决方案?
答案 0 :(得分:9)
为什么这个成员函数专门化会出错?
当实例化模板类A
(例如A<std::vector<int>>
)时,模板参数T
等于std::vector<int>
,而不是std::vector<T>
,这是一种专门化该功能的情况。不幸的是,这个can not be done with member functions as mentioned in the comments。
有更好的解决方案吗?
是;在c++17中,您可以像这样使用带有特征的if constexpr
来检查std::vector
。
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
这样,您只保留一个模板类,print()
将在编译时根据模板类型T
实例化相应的部分。
如果您无权使用C ++ 17,则另一个选择是 SFINAE 成员(自c++11起)。
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
如果我还有更多其他数据类型,例如自定义向量类,该怎么办 还是矩阵?我必须定义许多
is_xx_vector
吗?
您可以检查类型是否是所提供类型的特殊化,如下所示。这样,您可以避免为每种类型提供许多特征。 is_specialization
is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
print
函数可以在c++17中:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
答案 1 :(得分:5)
您需要实现一个使用向量作为模板参数的模板类。这对我有用。
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};
答案 2 :(得分:3)
您始终可以使用命名标签分派来检查模板用户提供的类型是否为向量。
A
下面是我使用命名标签调度作为解决问题的代码:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
成功编译
答案 3 :(得分:2)
您可以将打印调度到另一个成员函数(是否为static
)。例如:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};