ADL无法找到友元函数并导致链接器错误

时间:2017-12-27 02:50:06

标签: c++ argument-dependent-lookup

更新O'Neil发现了粗心错误:我使用operator!=定义了const,但在声明中省略了const。这当然也解释了为什么在类声明中放置完整的函数定义最初解决了问题。

我试图为C ++实现一个简单的标准库(用于学习目的),并且在调用与迭代器关联的operator==operator!=时遇到链接器错误通过矢量。

注意:我已经将很多代码包含在内,但您应该只能查看vector::insertoperator!=(iterator, iterator)和{{1}的内容除了底部所述的链接错误外,还要找出问题。

我已经实现了vector.hpp,省略了一些我认为不足以确定错误原因的定义:

main

实现了iterator.hpp

#ifndef VECTOR_HPP
#define VECTOR_HPP


#include "utility.hpp"   // stl::size_t
#include "memory.hpp"    // stl::Allocator
#include "iterator.hpp"


namespace stl {

template <typename T, typename A>
class VectorBase
{
public:
    typedef T           ValueType;
    typedef T&          Reference;
    typedef const T&    ConstReference;
    typedef T*          Pointer;
    typedef const T*    ConstPointer;
    typedef A           AllocatorType;
    typedef stl::size_t SizeType;

    VectorBase(SizeType, const AllocatorType&);
    ~VectorBase() { delete[] elements_; }

    AllocatorType   allocator_;
    Pointer         elements_ = nullptr;
    SizeType        size_ = 0;
    SizeType        capacity_ = 0;
};

template <typename T, typename A = stl::Allocator<T> >
class Vector : private VectorBase<T, A>
{
public:
    typedef typename VectorBase<T, A>::AllocatorType    AllocatorType;
    typedef stl::BidirectionalIterator<T>               Iterator;
    typedef const stl::BidirectionalIterator<T>         ConstIterator;
    typedef typename VectorBase<T, A>::ValueType        ValueType;
    typedef typename VectorBase<T, A>::Reference        Reference;
    typedef typename VectorBase<T, A>::ConstReference   ConstReference;
    typedef typename VectorBase<T, A>::Pointer          Pointer;
    typedef typename VectorBase<T, A>::ConstPointer     ConstPointer;
    typedef Vector<T, A>                                Self;
    typedef typename VectorBase<T, A>::SizeType         SizeType;

    // CONSTRUCTORS & DESTRUCTORS
    Vector(SizeType = 0,
           ValueType&& = ValueType(),
           const AllocatorType& = AllocatorType());
    Vector(const Self&);
    Vector(Self&&);
    Self& operator=(Self);

    // OPERATORS
    Reference       operator[](SizeType);
    ConstReference  operator[](SizeType) const;

    // MODIFIERS
    Iterator    insert(Iterator, ValueType);
    void        pop_back();
    void        push_back(ValueType);
    void        reserve(SizeType);

    template <typename T_, typename A_>
    friend void swap(Vector<T_, A_>&, Vector<T_, A_>&);

    // ACCESSORS
    Iterator        begin();
    ConstIterator   cbegin() const;
    Iterator        end();
    ConstIterator   cend() const;
};

template <typename T, typename A>
auto Vector<T, A>::insert(Iterator iterator, ValueType val) -> Iterator
{
    if (this->size_ == this->capacity_)
        reserve(2 * this->size_);
    for (auto it = this->end(); it != iterator; --it) {    // this is the problem line
    // for (auto it = this->end(); stl::operator!=(it, iterator); --it) {  // this fixes the linking issue
        auto temp(it);
        *temp = *--it;
        ++it;
    }
    *iterator = stl::move(val);
    return iterator;
}

// ...

}   // namespace stl

#endif  // VECTOR_HPP

最后test.cpp是

#ifndef ITERATOR_HPP
#define ITERATOR_HPP


#include "utility.hpp"


namespace stl {

template <typename T>
struct BidirectionalIterator
{
    typedef T                           ValueType;
    typedef T*                          Pointer;
    typedef const T*                    ConstPointer;
    typedef T&                          Reference;
    typedef const T&                    ConstReference;
    typedef BidirectionalIterator<T>    Self;

    BidirectionalIterator(Pointer ptr = nullptr) : current_(ptr) { }
    BidirectionalIterator(const Self& that) : current_(that.current_) { }
    BidirectionalIterator(Self&&);
    Self&       operator=(Self);

    Self&           operator++();
    Self            operator++(int);
    Self&           operator--();
    Self            operator--(int);
    Reference       operator*();
    ConstReference  operator*() const;
    Pointer         operator->();
    ConstPointer    operator->() const;

    template <typename T_>
    friend void swap(BidirectionalIterator<T_>&,
                     BidirectionalIterator<T_>&);

    template <typename T_>
    friend bool operator==(BidirectionalIterator<T_>&,
                           BidirectionalIterator<T_>&);

    template <typename T_>
    friend bool operator!=(BidirectionalIterator<T_>&,
                           BidirectionalIterator<T_>&);

    Pointer current_;
};

template <typename T>
bool operator==(const BidirectionalIterator<T>& first,
                const BidirectionalIterator<T>& second)
{ return first.current_ == second.current_; }

template <typename T>
bool operator!=(const BidirectionalIterator<T>& first,
                const BidirectionalIterator<T>& second)
{ return !(first == second); }

// ...

}   // namespace stl

#endif  // ITERATOR_HPP

然后当我尝试编译:

#include <iostream>
#include "../include/vector.hpp"


int main(void)
{
    stl::Vector<int> v;
    for (stl::size_t i = 0; i < 5; ++i)
        v.push_back(i);

    for (auto it = v.begin(); it != v.end(); ++it)
        std::cout << *it << std::endl;

    std::cout << std::endl;

    v.pop_back();

    for (auto it = v.begin(); it != v.end(); ++it)
        std::cout << *it << std::endl;

    std::cout << std::endl;

    auto it = v.begin();
    ++it;
    v.insert(it, 100);

    for (auto it = v.begin(); it != v.end(); ++it)
        std::cout << *it << std::endl;
}

我得到了

$ clang++ test.cpp -std=c++11   // I get the same issue with g++

但是,如果我在Undefined symbols for architecture x86_64: "bool stl::operator!=<int>(stl::BidirectionalIterator<int>&, stl::BidirectionalIterator<int>&)", referenced from: stl::Vector<int, stl::Allocator<int> >::insert(stl::BidirectionalIterator<int>, int) in test-dbe290.o ld: symbol(s) not found for architecture x86_64 中取出v.insert(it, 100);,该程序就可以了。此外,如果我留下main,并按上述评论中的说明修复v.insert(it, 100);(即,将vector::insert从非限定函数调用更改为operator!=限定函数调用) ,整个程序链接正常,并产生预期的结果。我似乎未能正确调用ADL,因此我查看了adl according to cppreference.com的详细信息,以查找添加到查找中的其他命名空间并找到:

  

2)对于类类型的参数(包括union),该集合包含       ...       d)添加到集合

中的类中最内层的封闭命名空间

由于stl中包含BidirectionalIterator,我认为stl应包含在查找中,并且不需要明确说明。

另一个有趣的事情是,如果我将stl::operator!=更改为在operator!=类中完全定义,即:

BidirectionalIterator

并恢复导致错误的先前代码:template <typename T_> friend bool operator!=(BidirectionalIterator<T_>& first, BidirectionalIterator<T_>& second) { return !(first == second); } 中的for (auto it = this->end(); it != iterator; --it) {vector::insert中的insert调用正常,但另一个用于循环(即{{ 1}})导致以下编译器错误:

main

为什么编译器在没有显式范围解析运算符的情况下能够在for (auto it = v.begin(); it != v.end(); ++it)中找到error: invalid operands to binary expression ('stl::BidirectionalIterator<int>' and 'Iterator' (aka 'BidirectionalIterator<int>')) for (auto it = v.begin(); it != v.end(); ++it) ?另外,为什么在operator!=的类声明中完全定义友元函数解决了第一个问题,但直接在stl中导致BidirectionalIterator实例的编译器错误?

0 个答案:

没有答案