c++ - ADL无法找到好友功能并导致链接器错误

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

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

注意:我已经包括了很多代码,可以使您更全面,但是除了声明的链接错误之外,您还应该能够看一下vector::insertoperator!=(iterator, iterator)main的内容。在底部,找出问题所在。

我已经实现了vector.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

然后实现iterator.hpp

#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

最后test.cpp是

#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并修复上面注释中指出的vector::insert (即,将operator!=从不合格的函数调用更改为stl合格的函数调用),整个程序可以很好地链接并产生预期的结果。 看来我无法正确调用ADL,所以我根据cppreference.com查看了adl的详细信息,以查找添加到查找中的其他名称空间并找到:

2)对于类类型的参数(包括并集),该集合包括... d)添加到集合中的类中最内层的命名空间

由于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::insertmaininsert调用工作正常,但另一个用于循环(即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)

如果没有显式范围解析运算符,编译器为什么不能在stl找到operator!= 此外,为什么在BidirectionalIterator的类声明中完全定义友好函数可以解决第一个问题,但是直接在main导致operator!=实例的编译器错误?

转载请注明来自askonline.tech,本文标题:c++ - ADL无法找到好友功能并导致链接器错误


 Top