【发布时间】:2016-02-12 10:46:51
【问题描述】:
标准内部人员您好,
我想将文件中的数字对读入 std::map 并使用附加的代码。现在,它可以工作了,但我对它的可靠性有几个疑问。
我的主要问题在于标有 (*) 的两行。在这里,模板 (i/o)stream_iterator<...> 被实例化。其中的代码如 (#)
std::pair<int, int> x;
some_istream >> x; // (##)
在带有 (##) 的行实际调用签名运算符的位置创建
std::istream & operator >> (std::istream & is, std::pair<int, int> & v);
现在 ADL 出现并决定在哪里寻找运算符 >>。它考虑当前命名空间和所有参数的命名空间。现在,由于所有参数都存在于命名空间 std 中,并且代码 (#) 本身也存在于 std 中(它在该命名空间中实例化,因为在那里定义了模板),std 是唯一考虑的命名空间。所以我肯定必须以某种方式让我的操作员在那个命名空间中可用,不是吗?
不幸的是,我不允许将新符号放入命名空间 std(我听说过)。因此,无论如何,我都在寻找侵入性最小的方法来归档我的目标。我发现我可以将我的操作符放在我自己的命名空间中,然后在我使用它的实现文件中将它宣布到 std 命名空间(参见 (**))(这是我使用模板 (*) 的地方)。
所以这是我的问题:
我是否“被允许”这样做?至少 gcc 不会向 std 命名空间添加新符号。
这样做是否“安全”?仅在实现文件中使用 (**) 不会损坏其他文件中的任何代码,是吗?
实现文件的末尾是执行它的“正确”位置还是我需要将 (**) 移动到 (***),即在使用模板之前那需要运营商吗?至少对于 gcc 和 vc 它是有效的。我怀疑来自模板实例化的代码实际上是在 (**) 之后写入文件末尾的,所以它可以工作。这是定义的行为还是仅定义了实现?
谢谢,
imix
代码:
my_io.h
#include <iostream>
#include <map>
#include <tuple>
namespace my {
std::istream & operator >> (std::istream & is, std::pair<int, int> & v);
std::ostream & operator << (std::ostream & os, std::pair<int, int> const & v);
std::istream & operator >> (std::istream & is, std::map<int, int> & m);
std::ostream & operator << (std::ostream & os, std::map<int, int> const & m);
}
io.cpp
#include "my_io.h"
#include <algorithm>
#include <iterator>
namespace my {
using namespace std;
istream & operator >> (istream & is, pair<int, int> & v) {
return is >> v.first >> v.second;
}
ostream & operator << (ostream & os, pair<int, int> const & v) {
return os << v.first << ": " << v.second;
}
// (***)
istream & operator >> (istream & is, map<int, int> & m) {
using It = istream_iterator<pair<int, int>>;
auto it_begin = It(is); // (*)
auto it_end = It();
auto it_insert = inserter(m, m.begin());
copy(it_begin, it_end, it_insert);
return is;
}
ostream & operator << (ostream & os, map<int, int> const & m) {
auto it = ostream_iterator<pair<int, int>>(os, "\n"); // (*)
copy(m.begin(), m.end(), it);
return os;
}
}
namespace std { // (**)
using my::operator >>;
using my::operator <<;
}
main.cpp
#include "my_io.h"
#include <sstream>
#include <string>
using namespace std;
namespace my {
void work() {
string input = "1 2 3 4 5 6";
istringstream is(input);
map<int, int> m;
is >> m;
cout << m;
}
}
int main() {
my::work();
return 0;
}
编辑: 解决方案(参见 Jonathan Walkley 的回答)
以下包装类型将解决该问题:
template <typename Key, typename T>
struct wrapped_pair {
std::pair<Key, T> x;
wrapped_pair(std::pair<Key const, T> x = std::pair<Key const, T>()) : x(x) { }
operator std::pair<Key const, T> () const { return x; }
};
我之前尝试过使用包装器类型,但我在隐式转换中弄错了const。现在,这是我的问题的有效解决方案:
my_io.h:
#include <iostream>
#include <map>
std::istream & operator >> (std::istream & is, std::map<int, int> & m);
std::ostream & operator << (std::ostream & os, std::map<int, int> const & m);
my_io.cpp:
#include "my_io.h"
#include <algorithm>
#include <iterator>
using namespace std;
struct wrapped_pair {
pair<int, int> x;
wrapped_pair(pair<int const, int> x = pair<int const, int>()) : x(x) { }
operator pair<int const, int>() const { return x; }
};
istream & operator >> (istream & is, wrapped_pair & v) {
return is >> v.x.first >> v.x.second;
}
ostream & operator << (ostream & os, wrapped_pair const & v) {
return os << v.x.first << ": " << v.x.second;
}
istream & operator >> (istream & is, map<int, int> & m) {
using It = istream_iterator<wrapped_pair>;
copy(It(is), It(), inserter(m, m.begin()));
return is;
}
ostream & operator << (ostream & os, map<int, int> const & m) {
auto it = ostream_iterator<wrapped_pair>(os, "\n");
copy(m.begin(), m.end(), it);
return os;
}
main.cpp:
#include "my_io.h"
#include <sstream>
#include <string>
using namespace std;
int main() {
string input = "1 2 3 4 5 6";
istringstream is(input);
map<int, int> m;
is >> m;
cout << m;
return 0;
}
我知道,我仍在劫持运营商 >> 和 << 以获得 std::map,但为了简单起见,我将其留在这里。
【问题讨论】:
-
您在
my命名空间中定义了运算符,并从my命名空间中使用它们(work函数在该命名空间中),并且您自己说“[i] t 考虑当前命名空间...”在my::work函数中,当前命名空间是my,与重载运算符的命名空间相同,因此它无需在std命名空间中添加任何内容即可工作。跨度> -
@JoachimPileborg:它起作用了,没错。但它在标有 ( * ) 的行中不起作用,其中来自 std 的模板被实例化。尝试删除标记(**)的块,你会看到。
标签: c++ templates namespaces std argument-dependent-lookup