简体   繁体   English

为什么不:: boost :: tie与BOOST_FOREACH一起工作?

[英]Why doesn't ::boost::tie work with BOOST_FOREACH?

I want to use BOOST_FOREACH to iterate over a boost::ptr_map , and came across this neat-looking solution . 我想使用BOOST_FOREACH来迭代boost::ptr_map ,并且遇到了这个看起来很整洁的解决方案 I would prefer using this for better readability, as against the other solutions given. 我希望使用它来提高可读性,与其他解决方案相比。 I wrote the following code: 我写了以下代码:

boost::ptr_map<int, std::string> int2strMap;
int x = 1;
int2strMap.insert(x, new std::string("one"));
int one;
std::string* two;
BOOST_FOREACH(::boost::tie(one, two), int2strMap)
{
   std::cout << one << two << std::endl;
}

However, this fails to compile, and gives me the below error (The full error message has several more lines, let me know if I should paste them.): 但是,这无法编译,并给我以下错误(完整的错误消息有几行,请告诉我是否应该粘贴它们。):

error: no match for 'operator=' (operand types are 'boost::tuples::detail::tie_mapper<int, std::basic_string<char>*, void, void, void, void, void, void, void, void>::type {aka boost::tuples::tuple<int&, std::basic_string<char>*&, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>}' and 'boost::iterators::iterator_reference<boost::ptr_map_iterator<std::_Rb_tree_iterator<std::pair<const int, void*> >, int, std::basic_string<char>* const> >::type {aka boost::ptr_container_detail::ref_pair<int, std::basic_string<char>* const>}')
BOOST_FOREACH(::boost::tie(one, two), int2strMap)

It appears that the suggested solution works for a handful of people, and I am unable to figure out why it doesn't work for me. 似乎建议的解决方案适用于少数人,我无法弄清楚为什么它对我不起作用。 What am I doing wrong here? 我在这做错了什么?

(Note: I am working on a prehistoric project, so stuck with using C++03. g++ version: 4.8.4) (注意:我正在进行一个史前项目,因此坚持使用C ++ 03。g ++版本:4.8.4)

The question really should be "Why doesn't boost::tie work with boost::ptr_map (or rather the result of dereferencing its iterator)?" 问题应该是“为什么不boost::tieboost::ptr_map (或者更确切地说是取消引用其迭代器的结果)?” -- BOOST_FOREACH is quite innocent in all this. - BOOST_FOREACH在这一切中都是无辜的。

Investigation 调查

If we look at the version history of Boost , we can see that Tuple appears in version 1.24.0 and Pointer Container in version 1.33.0. 如果我们查看Boost版本历史 ,我们可以看到Tuple出现在版本1.24.0中,而Pointer Container出现在版本1.33.0中。

Tuple 元组

Relevant tuple related code in github: github中相关的元组相关代码:

Studying the code, we can make the following observations: 研究代码,我们可以做出以下观察:

  • tie has always created a tuple [1] [2] tie总是创造一个tuple [1] [2]

  • tuple has always derived from template cons [1] [2] tuple总是来自模板cons [1] [2]

  • tuple (and cons ) always had assignment operators taking either a cons (ie another tuple ) [1] [2] or a std::pair [1] [2] -- nothing else. tuple (和cons )总是让赋值运算符采用一个cons (即另一个tuple[1] [2]std::pair [1] [2] - 没有别的。

Pointer Container 指针容器

Relevant pointer container related code in github: github中相关的指针容器相关代码:

Studying the code, we can make the following observations: 研究代码,我们可以做出以下观察:

  • In first two releases (1.33.x), dereferencing the iterator gave us a reference to the value [1] [2] 在前两个版本(1.33.x)中,取消引用迭代器为我们提供了值[1] [2]的引用
  • Since the third release (1.34.0), we get a ref_pair , which somewhat looks like a std::pair , but really isn't [1] [2] [3] 从第三个版本(1.34.0)开始,我们得到一个ref_pair ,它有点像std::pair ,但实际上不是[1] [2] [3]

Conclusion 结论

We can eliminate BOOST_FOREACH by just doing one iteration, and still get the same error: 我们可以通过只进行一次迭代来消除BOOST_FOREACH ,但仍然会得到相同的错误:

boost::tie(one, two) = *int2strMap.begin();

Based on what we learned earlier, we know this is equivalent to 根据我们之前学到的知识,我们知道这相当于

boost::tuple<int&, std::string*&>(one, two) = *int2strMap.begin();

We also know that *int2strMap.begin() will result in either a std::string reference, or a ref_pair . 我们也知道*int2strMap.begin()将导致std::string引用或ref_pair

Since tuple has no assignment operator that would take either of those, the proposed snippet can not compile with any existing version of Boost. 由于元组没有可以使用其中任何一个的赋值运算符,因此建议的代码段无法使用任何现有版本的Boost进行编译。


Workaround 解决方法

Taking inspiration from the implementation of boost::tuple and boost::tie , we can write a simple reference_pair template that holds two references and allows assignment of anything that looks like a pair (ie has members first and second ), along with a helper tie function that will create an instance of reference_pair . boost::tupleboost::tie的实现中获得灵感,我们可以编写一个简单的reference_pair模板,该模板包含两个引用并允许分配看起来像一pair的任何东西(即具有成员firstsecond ),以及一个帮助器tie函数,它将创建reference_pair的实例。

Sample Code 示例代码

#include <boost/ptr_container/ptr_map.hpp>
#include <boost/foreach.hpp>
#include <iostream>

namespace {

template<class T0, class T1>
struct reference_pair
{
    T0& first;
    T1& second;

    reference_pair(T0& t0, T1& t1) : first(t0), second(t1) {}

    template<class U>
    reference_pair& operator=(const U& src) {
        first = src.first;
        second = src.second;
        return *this;
    }
};

template<class T0, class T1>
inline reference_pair<T0, T1> tie(T0& t0, T1& t1)
{
    return reference_pair<T0, T1>(t0, t1);
}

}

int main()
{
    boost::ptr_map<int, std::string> int2strMap;
    int n(0);
    int2strMap.insert(n, new std::string("one"));
    int2strMap.insert(++n, new std::string("two"));
    int2strMap.insert(++n, new std::string("three"));

    int one;
    std::string* two;

    BOOST_FOREACH(tie(one, two), int2strMap)
    {
       std::cout << one << " " << *two << std::endl;
    }
}

Live on Coliru 住在Coliru

Console Output 控制台输出

0 one
1 two
2 three

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM