问题
I am trying to use boost::lexical_cast
on a std::pair<int, int>
.
#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>
namespace my
{
// When my_pair is a user defined type, this program compiles
// and runs without any problems.
// When declaring my_pair as an alias of std::pair<int, int>,
// it fails to compile
/*
struct my_pair
{
int first;
int second;
};
*/
using my_pair = std::pair<int, int>;
std::istream& operator>>(std::istream& stream, my_pair& pair)
{
stream >> pair.first;
stream >> std::skipws;
stream >> pair.second;
return stream;
}
}
int main()
{
my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
std::cout << p.first << " " << p.second << std::endl;
return 0;
}
If I understand right, in order to make ADL work, the operator>> has to be in the same namespace as my_pair, so std.
Doing so, would result in undefined behavior, because I would be adding functions to the namespace std.
I would like to avoid inheritance, as in struct my_pair : std::pair<int, int>
.
What could be the solution to this problem?
I am using clang++-3.6 on OS X.
回答1:
Instead of ADL-hooking on the value to stream, you could overload on the stream (tagging it in some way):
int main() {
std::map<int, std::string> standback { { 42, "I'm gonna try" }, { 1729, "science" } };
streaming::tag_ostream out = std::cout;
for (auto& entry : standback)
out << entry << "\n";
}
This way, you can ADL-hook on a namespace that is under your control. You can make the tagging more generic (think auto out = streaming::tag(std::cout)
).
Now, a simple implementation of that could look like
namespace streaming {
template <typename T>
struct tag : std::reference_wrapper<T> {
using std::reference_wrapper<T>::reference_wrapper;
};
using tag_ostream = tag<std::ostream>;
template <typename T1, typename T2>
static inline tag_ostream operator<<(tag_ostream os, std::pair<T1, T2> const& p) {
os.get() << "std::pair{" << p.first << ", " << p.second << "}";
return os;
}
template <typename Other>
static inline tag_ostream operator<<(tag_ostream os, Other const& o) {
os.get() << o;
return os;
}
}
See it Live On Coliru, which prints:
std::pair{42, I'm gonna try}
std::pair{1729, science}
回答2:
I know you said that you didn't want this, but I definitely would use inheritance:
#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>
namespace my
{
struct my_pair : std::pair<int, int> {};
std::istream& operator>>(std::istream& stream, my_pair& pair)
{
stream >> pair.first;
stream >> std::skipws;
stream >> pair.second;
return stream;
}
}
int main()
{
my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
std::cout << p.first << " " << p.second << std::endl;
}
(live demo)
Your my::my_pair
literally is-a std::pair<int, int>
; you just need it to be a distinct type in your own namespace. This is what inheritance is for.
I'm just leaving this here to show how easy it is to do and explain why I think you should do it.
回答3:
Doing so, would result in undefined behavior, because I would be adding functions to the namespace std.
I would like to avoid inheritance, as in struct my_pair : std::pair.
I wanted to say "inheritance", but you dismiss it ...
You could use encapsulation, simply adding another strong type over std::pair<int,int>
(but in such a trivial case, you may be better off with the custom structure - your commented code):
struct my_pair
{
std::pair<int,int> value;
// TODO: add any access interface here
};
std::istream& operator>>(std::istream& stream, my_pair& pair)
{
stream >> pair.value.first;
stream >> std::skipws;
stream >> pair.value.second;
return stream;
}
Infact, you probably should do this, because std::pair is more of a building block, not something that should be used to represent semantic information (and not something that should be dirrectly printable to a stream).
来源:https://stackoverflow.com/questions/30642133/overload-operator-for-stdpairint-int