【问题标题】:pass by reference unexpected behaviour with reference parameter使用引用参数通过引用传递意外行为
【发布时间】:2014-12-01 00:58:56
【问题描述】:

尝试将基于 for 循环的范围内的 const 引用值传递给 采用 const 参考值的函数。查看基于 for 循环的范围内的注释以了解我在说什么。我的 'unordered_vector' 实现似乎不是问题,这是我最初的想法,因为它在函数外部正确打印。为什么会失败?如果我按值传递它会起作用,所以我在这里有点困惑。看起来它引用的引用似乎不是正确的行为。

void println(const vec3& p)
{
    std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}

void printlnvalue(vec3 p)
{
    std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}

const int vector_length = 7;

int _tmain(int argc, _TCHAR* argv[])
{
    unordered_vector<vec3> vectors(vector_length);

    for (int i = 0; i < vector_length; ++i)
    {
        vectors.push_back(vec3(0.1f*i, 0.2f*i, 0.3f*i));
    }

    for (const auto& i : vectors)
    {
        //Prints correctly
        printlnvalue(i);
        //Function that does same thing but doesn't print correctly.
        println(i);
    }

    return 0;
}

输出

<0,0,0>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.1,0.2,0.3>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.2,0.4,0.6>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.3,0.6,0.9>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.4,0.8,1.2>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.5,1,1.5>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.6,1.2,1.8>
<8.68805E-044,2.66467E-038,4.12969E-039>

unordered_vector 声明

template<typename T>
class unordered_vector
{
private:
    T* m_data;
    size_t m_size;
    size_t m_capacity;
public:
    typedef unordered_vector_iterator<T> iterator;
    typedef unordered_vector_iterator<T> const const_iterator;
    typedef ptrdiff_t difference_type;
    typedef size_t size_type;
    typedef T value_type;
    typedef T* pointer;
    typedef T& reference;

    size_t size(void) const;
    size_t capacity(void) const;
    bool empty(void) const;

    void resize(size_t count);
    void reserve(size_t count);

    void push_back(const T& val);

    const_iterator begin(void) const;
    const_iterator end(void) const;

    void shrink_to_fit(void);

    void erase(size_t i);
    void erase(const T& val);
    void erase_all(const T& val);

    T& operator[](size_t i);
    T operator[](size_t i) const;
    unordered_vector& operator=(const unordered_vector& copy);
    unordered_vector& operator=(unordered_vector&& mov);

    unordered_vector(void);
    unordered_vector(size_t _size);
    unordered_vector(unordered_vector& copy);
    unordered_vector(unordered_vector&& mov);
    ~unordered_vector(void);
};

unordered_vector 定义

template<typename T>
/*
    Returns the number of active units within
    the unordered_vector.
*/
size_t unordered_vector<T>::size(void) const 
{
    return m_size; 
}

template<typename T>
/*
    Returns the potential size of the vector
    before it would have to resize.
*/
size_t unordered_vector<T>::capacity(void) const { return m_capacity; }

template<typename T>
/*
    Returns true if no active units are within the vector. size() == 0
*/
bool unordered_vector<T>::empty(void) const 
{
    return m_size == 0; 
}

template<typename T>
/*
    This resizes the vector where anything between the parameter and 
    size is part of the capacity.
*/
void unordered_vector<T>::reserve(size_t count)
{
    if (count > m_capacity)
    {
        //Set capacity to new value
        m_capacity = count;
        //Make new array
        T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
        //Copy preexisting data byte for byte
        if (m_data != nullptr)
        {
            //Copy data from previous buffer to new buffer
            memcpy(new_ptr, m_data, sizeof(T)*m_size);
            //Delete previous buffer
            free(m_data);
        }
        //Set pointer to the new array
        m_data = new_ptr;
    }
}

template<typename T>
/*
    Will resize the vector but each of the units is
    active if the buffer is increased. If the size
    is reduced then items at the end are truncated.
*/
void unordered_vector<T>::resize(size_t count)
{
    if (count > m_capacity)
    {
        //Set capacity to new value
        m_capacity = count;
        //Make new array
        T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
        //Copy preexisting data byte for byte
        if (m_data != nullptr)
        {
            //Copy data from previous buffer to new buffer
            memcpy(new_ptr, m_data, sizeof(T)*m_size);
            //Delete previous buffer
            free(m_data);
        }
        //Set pointer to the new array
        m_data = new_ptr;
        //Make default values at each location
        for (; m_size < count; ++m_size)
        {

        }
    }
    else if (count < m_capacity)
    {
        if (count < m_size) m_size = count;
        //Set capacity to new value
        m_capacity = count;
        //Make new array
        T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
        //Copy preexisting data byte for byte
        if (m_data != nullptr)
        {
            //Copy data from previous buffer to new buffer
            memcpy(new_ptr, m_data, sizeof(T)*m_size);
            //Delete previous buffer
            free(m_data);
        }
        //Set pointer to the new array
        m_data = new_ptr;
    }
}

template<typename T>
/*
    Shrinks capacity so capacity() == size().
*/
void unordered_vector<T>::shrink_to_fit(void)
{
    if (m_size != m_capacity && m_data != nullptr)
    {
        m_capacity = m_size;
        //Make new array
        T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
        //Copy preexisting data byte for byte
        memcpy(new_ptr, m_data, sizeof(T)*m_size);
        //Delete previous buffer
        if (m_data != nullptr)
            free(m_data);
        //Set pointer to the new array
        m_data = new_ptr;
    }
}

template<typename T>
/*
    Will copy a value into the next open space of the vector.
    If there isn't room for an additional item it will resize
    the vector to accomadate it.
*/
void unordered_vector<T>::push_back(const T& val)
{
    //If we don't have any more room we need
    //to resize the array
    if (m_size == m_capacity)
    {
        m_capacity = m_capacity == 0 ? 8 : m_capacity * 2;
        //Make new array
        T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
        if (m_data != nullptr)
        {
            //Copy preexisting data byte for byte
            memcpy(new_ptr, m_data, sizeof(T)*m_size);
            //Delete previous buffer
            free(m_data);
        }
        //Set pointer to the new array
        m_data = new_ptr;
    }

    m_data[m_size++] = val;
}

template<typename T>
/*
    Random accessor for writing to array.
*/
T& unordered_vector<T>::operator[](size_t i)
{
    assert(i >= 0 && i < m_size);
    return m_data[i];
}

template<typename T>
/*
    Random accessor for reading from array.
*/
T unordered_vector<T>::operator[](size_t i) const
{
    assert(i >= 0 && i < m_size);
    return m_data[i];
}

template<typename T>
/*
    Constant time erase. It reorders the vector
    internally to allow this.
*/
void unordered_vector<T>::erase(size_t i)
{
    assert(i >= 0 && i < m_size);

    if (i == m_size - 1)
        m_size -= 1;
    else
    {
        m_data[i] = m_data[m_size - 1];
        m_size -= 1;
    }
}

template<typename T>
/*
    Will remove the first value that matches 
    val if it exists. In the event no value is
    found the request is ignored.
*/
void unordered_vector<T>::erase(const T& val)
{
    for (size_t i = 0; i < m_size; ++i)
    {
        if (m_data[i] == val)
        {
            if (i == m_size - 1)
                m_size -= 1;
            else
            {
                m_data[i] = m_data[m_size - 1];
                m_size -= 1;
            }

            break;
        }
    }
}

template<typename T>
/*
    Will remove all values that match the parameter.
    If no items match the parameter then the request
    is ignored.
*/
void unordered_vector<T>::erase_all(const T& val)
{
    for (size_t i = 0; i < m_size; ++i)
    {
        if (m_data[i] == val)
        {
            if (i == m_size - 1)
                m_size -= 1;
            else
            {
                m_data[i] = m_data[m_size - 1];
                m_size -= 1;
                //Haven't checked the back yet
                //so we need to recheck i
                i--;
            }
        }
    }
}

template<typename T>
/*
    Initializes the vector with 0 capacity.
*/
unordered_vector<T>::unordered_vector(void)
    : m_data(nullptr), m_size(0), m_capacity(0) {}

template<typename T>
/*
    Initializes the vector with a capacity() equal to
    the parameter.
*/
unordered_vector<T>::unordered_vector(size_t count)
    : m_data(nullptr), m_size(0), m_capacity(0)
{
    reserve(count);
}

template<typename T>
/*
    Will copy data from another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>& vector)
{
    if (vector.m_capacity > 0)
    {
        m_data = malloc(sizeof(T)*vector.m_capacity);

        if (vector.m_size > 0)
        memcpy(m_data, vector.m_data, sizeof(T)*vector.m_size);
    }

    m_capacity = vector.m_capacity;
    m_size = vector.m_size;
}

template<typename T>
/*
    Move constructor to effeciently transfer data between
    a temporary and another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>&& mov)
{
    m_data = mov.m_data;
    m_capacity = mov.m_capacity;
    m_size = mov.m_size;
    mov.m_data = nullptr;
}

template<typename T>
/*
    Destructor that deallocates memory
    if any was allocated. Will not deallocate
    memory if move semantic was invoked.
*/
unordered_vector<T>::~unordered_vector(void)
{
    if (m_data != nullptr)
    {
        free(m_data);
        m_data = nullptr;
    }

    m_size = 0;
    m_capacity = 0;
}

template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(const unordered_vector<T>& copy)
{
    if (m_data != nullptr)
    {
        free(m_data);
        m_data = nullptr;
    }

    if (copy.m_capacity > 0)
    {
        m_data = malloc(sizeof(T)*copy.capacity());

        if (copy.m_size > 0)
            memcpy(m_data, copy.m_data, sizeof(T)*copy.m_size)
    }

    m_capacity = copy.m_capacity;
    m_size = copy.m_size;

    return *this;
}

template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(unordered_vector<T>&& mov)
{
    if (m_data != nullptr)
        free(m_data);

    m_data = mov.m_data;
    m_capacity = mov.m_capacity;
    m_size = mov.m_size;
    mov.m_data = nullptr;
}

template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::begin(void) const
{
    return unordered_vector_iterator<T>(this, 0);
}

template<typename T>
unordered_vector_iterator<T> const  unordered_vector<T>::end(void) const
{
    return unordered_vector_iterator<T>(this, m_size);
}

迭代器声明

template<typename T>
class unordered_vector_iterator
{
private:
    const unordered_vector<T>* const m_container;
    size_t m_index;
public:
    unordered_vector_iterator& operator++(void);
    unordered_vector_iterator& operator++(int);
    unordered_vector_iterator& operator--(void);
    unordered_vector_iterator& operator--(int);
    T& operator*(void) const;
    T& operator->(void) const;
    bool operator==(const unordered_vector_iterator& iter) const;
    bool operator!=(const unordered_vector_iterator& iter) const;
    unordered_vector_iterator(const unordered_vector<T>* const container, size_t index)
        : m_container(container), m_index(index) {}
};

迭代器定义

template<typename T>
bool unordered_vector_iterator<T>::operator==(const unordered_vector_iterator<T>& iter) const
{
    return iter.m_index == m_index && iter.m_container == m_container;
}

template<typename T>
bool unordered_vector_iterator<T>::operator!=(const unordered_vector_iterator<T>& iter) const
{
    return iter.m_index != m_index || iter.m_container != m_container;
}

template<typename T>
unordered_vector_iterator<T>&  unordered_vector_iterator<T>::operator++(void)
{
    ++m_index;
    return *this;
}

template<typename T>
unordered_vector_iterator<T>&  unordered_vector_iterator<T>::operator++(int)
{
    ++m_index;
    return *this;
}

template<typename T>
unordered_vector_iterator<T>&  unordered_vector_iterator<T>::operator--(void)
{
    --m_index;
    return *this;
}

template<typename T>
unordered_vector_iterator<T>&  unordered_vector_iterator<T>::operator--(int)
{
    --m_index;
    return *this;
}

template<typename T>
T& unordered_vector_iterator<T>::operator*(void) const
{
    return (*m_container)[m_index];
}

template<typename T>
T& unordered_vector_iterator<T>::operator->(void) const
{
    return (*m_container)[m_index];
}

vec3 声明

struct vec3
{
public:
    float x, y, z;
    //vector functions
    void  normalize(void);
    float magnitude(void) const;
    float magnitudeSq(void) const;

    bool operator==(vec3 o) const;
    bool operator!=(vec3 o) const;
    //Constructors
    vec3(float _x, float _y, float _z)
        : x(_x), y(_y), z(_z) {}
    vec3(float s)
        : x(s), y(s), z(s) {}
    vec3() 
    : x(0.f), y(0.f), z(0.f) {}
};

【问题讨论】:

  • 看起来不错。 unordered_vectorvec3的定义是什么?
  • unordered_vector的索引操作符是如何实现的?
  • 在上面添加了 unordered_vector 定义

标签: c++ function reference call


【解决方案1】:

您可能需要在unordered_vector 中声明另一个运算符以返回const T&amp;。这就是它在 STL vector 类中的声明方式:

typedef const_reference const T&;
const_reference operator[](size_t idx) const;

【讨论】:

  • 可能在做某事。但是,现在我得到一个编译器错误,我不确定如何修复。 unordered_vector.h(151): 错误 C2440: 'return' : 无法从 'const vec3' 转换为 'const vec3 &'
  • 将其添加到您的问题中。
【解决方案2】:

代码的问题是 const 方法不返回引用值。

为了使代码正常工作,由于某种原因,它必须返回一个非常量的参考值。不是一个很好的答案,但现在通过将 const 索引运算符更改为 this 可以完美运行。

template<typename T>
/*
    Random accessor for reading from array.
*/
T& unordered_vector<T>::operator[](size_t i) const
{
    assert(i >= 0 && i < m_size);
    return m_data[i];
}

【讨论】:

    猜你喜欢
    • 2018-09-23
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多