`operator<<` 在容器上同时适用于值和引用
`operator<<` on a container which works for both values and references
有一个容器 class container
是模板化的,所以它可以包含任何东西。
我想添加将其内容打印到 std::ostream
的功能,所以我覆盖了 operator<<
.
但是这有一个缺点:如果容器包含引用(或指针),该方法只打印地址而不是真实信息。
请考虑这个演示问题的简单测试代码:
#include <iostream>
#include <deque>
template<typename T, bool is_reference = false>
class container {
public:
container() {}
void add(T a) { queue.push_back(a); }
friend std::ostream& operator<<(std::ostream& out, const container<T, is_reference>& c) {
for (const T& item : c.queue) {
if (is_reference) out << *item; else out << item;
out << ",";
}
return out;
}
private:
std::deque<T> queue;
};
int main() {
//Containers
container<int*, true> myContainer1;
container<int> myContainer2;
int myA1(1);
int myA2(10);
myContainer1.add(&myA1);
myContainer1.add(&myA2);
myContainer2.add(myA1);
myContainer2.add(myA2);
std::cout << myA1 << std::endl;
std::cout << myA2 << std::endl;
std::cout << myContainer1 << std::endl;
std::cout << myContainer2 << std::endl;
return 0;
}
我想在模板中提供一个额外的 is_reference
布尔值来调整 operator<<
。
然而,如果我有 值类型 容器,这会导致早期编译器错误。
我怎样才能完成这项工作?
如果我将打印机行更改为
out << item << ",";
代码编译并打印如下:
1
10
0x7ffd77357a90,0x7ffd77357a94,
1,10,
显然我的目标是得到这个结果:
1
10
1,10,
1,10,
(如何)我可以轻松实现吗?
要在 item
和 *item
之间进行条件选择,您需要一个支持 C++17 标准(或更高版本)的编译器,然后使用 if constexpr (...)
声明。
此外,您可以使用 std::is_pointer
检查包含的类型是否为指针,而不是向您的模板添加 'flag':
#include <iostream>
#include <deque>
#include <type_traits>
template<typename T>
class container {
public:
container() {}
void add(T a) { queue.push_back(a); }
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
for (const T& item : c.queue) {
if constexpr (std::is_pointer<T>::value) {
out << *item;
}
else {
out << item;
}
out << ",";
}
return out;
}
private:
std::deque<T> queue;
};
您可以根据 std::is_pointer<T>::value
是 true
还是 false
使用 SFINAE select 重载。这已经适用于 C++11:
#include <iostream>
#include <deque>
template<typename T>
class container {
public:
// container() {} // dont define empty constructor when not needed
// or declare it as = default
void add(const T& a) { queue.push_back(a); } // should take const&
template <typename U = T, typename std::enable_if< std::is_pointer<U>::value,bool>::type=true>
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
std::cout << "is pointer\n";
}
template <typename U = T, typename std::enable_if< ! std::is_pointer<U>::value,bool>::type=true>
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
std::cout << "is not pointer\n";
}
private:
std::deque<T> queue;
};
int main() {
//Containers
container<int*> myContainer1;
container<int> myContainer2;
std::cout << myContainer1 << std::endl;
std::cout << myContainer2 << std::endl;
}
is pointer
is not pointer
从 C++17 开始,您可以使用 constexpr if
。此外,由于 C++17 有 std::is_pointer_v
并且由于 C++14 有 std::enable_if_t
,两者都会使代码不那么冗长。
有一个容器 class container
是模板化的,所以它可以包含任何东西。
我想添加将其内容打印到 std::ostream
的功能,所以我覆盖了 operator<<
.
但是这有一个缺点:如果容器包含引用(或指针),该方法只打印地址而不是真实信息。
请考虑这个演示问题的简单测试代码:
#include <iostream>
#include <deque>
template<typename T, bool is_reference = false>
class container {
public:
container() {}
void add(T a) { queue.push_back(a); }
friend std::ostream& operator<<(std::ostream& out, const container<T, is_reference>& c) {
for (const T& item : c.queue) {
if (is_reference) out << *item; else out << item;
out << ",";
}
return out;
}
private:
std::deque<T> queue;
};
int main() {
//Containers
container<int*, true> myContainer1;
container<int> myContainer2;
int myA1(1);
int myA2(10);
myContainer1.add(&myA1);
myContainer1.add(&myA2);
myContainer2.add(myA1);
myContainer2.add(myA2);
std::cout << myA1 << std::endl;
std::cout << myA2 << std::endl;
std::cout << myContainer1 << std::endl;
std::cout << myContainer2 << std::endl;
return 0;
}
我想在模板中提供一个额外的 is_reference
布尔值来调整 operator<<
。
然而,如果我有 值类型 容器,这会导致早期编译器错误。
我怎样才能完成这项工作?
如果我将打印机行更改为
out << item << ",";
代码编译并打印如下:
1
10
0x7ffd77357a90,0x7ffd77357a94,
1,10,
显然我的目标是得到这个结果:
1
10
1,10,
1,10,
(如何)我可以轻松实现吗?
要在 item
和 *item
之间进行条件选择,您需要一个支持 C++17 标准(或更高版本)的编译器,然后使用 if constexpr (...)
声明。
此外,您可以使用 std::is_pointer
检查包含的类型是否为指针,而不是向您的模板添加 'flag':
#include <iostream>
#include <deque>
#include <type_traits>
template<typename T>
class container {
public:
container() {}
void add(T a) { queue.push_back(a); }
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
for (const T& item : c.queue) {
if constexpr (std::is_pointer<T>::value) {
out << *item;
}
else {
out << item;
}
out << ",";
}
return out;
}
private:
std::deque<T> queue;
};
您可以根据 std::is_pointer<T>::value
是 true
还是 false
使用 SFINAE select 重载。这已经适用于 C++11:
#include <iostream>
#include <deque>
template<typename T>
class container {
public:
// container() {} // dont define empty constructor when not needed
// or declare it as = default
void add(const T& a) { queue.push_back(a); } // should take const&
template <typename U = T, typename std::enable_if< std::is_pointer<U>::value,bool>::type=true>
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
std::cout << "is pointer\n";
}
template <typename U = T, typename std::enable_if< ! std::is_pointer<U>::value,bool>::type=true>
friend std::ostream& operator<<(std::ostream& out, const container<T>& c) {
std::cout << "is not pointer\n";
}
private:
std::deque<T> queue;
};
int main() {
//Containers
container<int*> myContainer1;
container<int> myContainer2;
std::cout << myContainer1 << std::endl;
std::cout << myContainer2 << std::endl;
}
is pointer
is not pointer
从 C++17 开始,您可以使用 constexpr if
。此外,由于 C++17 有 std::is_pointer_v
并且由于 C++14 有 std::enable_if_t
,两者都会使代码不那么冗长。