6

我有一些问题让我感到奇怪。我已经 typedef'ed astd::vector其中包含一些自己的类:

typedef std::vector<data::WayPoint> TWayPointList;

这是结构内部的嵌套类型,DataHandler它在某些命名空间中具有抵抗力data

所以,现在我想打印出向量的单个内容。为此,我的想法是重载 << 运算符并循环遍历 typedef'ed 向量的单个元素。所以我在结构中声明了以下输出运算符DataHandler

namespace data
{
    structure DataHandler
    {

        // ... some code
        typedef std::vector<data::WayPoint> TWayPointList;

        // ... some more code

        /**
         * @brief Globally overloaded output operator
         *
         * @param[in] arOutputStream Reference to output stream.
         * @param[in] arWayPointList WayPoint which should be printed to output stream.
         */
         LIB_EXPORTS friend std::ostream& operator<<(std::ostream& arOutputStream, const data::DataHandler::TWayPointList& arWayPointList);
    } // structure DataHandler
} // namespace data

并在相应的源文件中定义它:

namespace data
{
   std::ostream& operator<<(std::ostream& arOutputStream, const DataHandler::TWayPointList& arWayPointList)
    {
        for(DataHandler::TWayPointList::const_iterator lIterator = arWayPointList.begin(); lIterator < arWayPointList.end(); ++lIterator)
        {
            arOutputStream << *lIterator << std::endl;
        }

        return arOutputStream;
    }
} // namespace data

这编译得很好。但是如果我添加这样的东西

int main(int argc, char *argv[])
{
    // create Waypoint
    data::WayPoint lWayPoint;

    // create waypoint list
    data::DataHandler::TWayPointList lWayPointList;

    // append two elements
    lWayPointList.push_back(lWayPoint);
    lWayPointList.push_back(lWayPoint);

    std::cout << lWayPointList << std::endl;

    return 0;
}

在我testmain.cpp的 中,编译器提到,它找不到正确的operator<<(并做出很多假设,它找到了哪一个......包括我自己在其他类中定义的一些)。像这样的一些错误

src/main.cpp:107: error: no match for 'operator<<' in 'std::cout << lWayPointList'
src/main.cpp:107:18: note: candidates are:
... a long list of canditates...

我认为这与 ADL 有关,但我没有明白这一点。

那么,有什么想法和建议可以让代码正常工作吗?

[编辑] 我在源代码和错误输出中添加了一些文件以进行澄清。

4

2 回答 2

6

朋友声明在具有此类朋友声明的类的命名空间中的命名空间级别声明一个函数。从运算符的定义来看,您似乎是在全局命名空间中定义它(顺便说一句,您在朋友声明中的评论说的是,太糟糕的编译器不阅读评论)。您需要operator<<在正确的命名空间中定义:

std::ostream& mkilib::operator<<(std::ostream& arOutputStream,
            /*^^^^^^^^*/         const mkilib::DataHandler::TWayPointList& arWayPointList)

或者:

namespace mkilib {
    std::ostream& operator<<(std::ostream& arOutputStream,
                             const DataHandler::TWayPointList& arWayPointList) {...}
}

在您的程序中,有两个operator<<声明了TWayPointList对象,一个在全局命名空间中(定义是一个自我声明),一个在::mkilib命名空间中(来自朋友声明)。依赖于参数的查找正在查找 中的那个::mkilib,但从未在代码中定义过。


更新后,这似乎不是真正的问题,因为编译器无法找到重载(上面的答案是关于已编译但未链接的代码)。您已经从您的代码更改为您对命名空间的要求。如果在同一个命名空间中定义了 和 ,那么 ADL 将找到正确的重载Waypoint。请注意,定义的名称空间没有任何作用。operator<<std::vector<Waypoint>DataHandler


实际上,现在我考虑一下,原来的答案确实适用。友元声明对查找没有任何影响,因为 ADL 不会在内部DataHandler搜索该运算符,因此唯一的声明operator<<是定义中的自我声明。

请注意,朋友声明在命名空间级别声明了一个实体,但该声明仅具有朋友声明的类中可见。

一条建议:避免使用指令,它们只会带来混乱和痛苦。如果需要,重新打开命名空间或限定标识符......使用指令使查找的推理更加复杂。

于 2012-10-09T20:23:40.423 回答
1

正如我从您的代码片段中看到的那样,您不是using namespace data,但您的运算符是在data命名空间中定义的。你可以使用data命名空间,但我相信你有自己的理由。

您可以在命名空间之外定义一个通用运算符,而不是声明特定于您的数据类型的运算符,如下所示:

template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& list)
{
    for(std::vector<T>::const_iterator iter = list.begin(); iter != list.end(); ++iter)
    {
        out << *iter;
    }
}
于 2012-10-09T20:54:37.510 回答