由ZeroMQ一處編譯錯誤,引申的一個C++編譯規範

最近在編譯ZeroMQ源碼時, 出現了一個編譯錯誤,其本質是涉及到C++的一個編譯規範.編譯出錯的源碼文件爲:

/*
    Copyright (c) 2007 FastMQ Inc.

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __ZMQ_ENCODER_HPP_INCLUDED__
#define __ZMQ_ENCODER_HPP_INCLUDED__

#include <assert.h>
#include <algorithm>

#include "dispatcher_proxy.hpp"
#include "cmsg.hpp"

namespace zmq
{

    template <typename T> class encoder_t
    {
    public:

        encoder_t (dispatcher_proxy_t *proxy_, int source_thread_id_,
              size_t chunk_size_) :
            proxy (proxy_),
            source_thread_id (source_thread_id_),
            chunk_size (chunk_size_)
        {
            init_cmsg (msg);
        }

        ~encoder_t ()
        {
            free_cmsg (msg);
        }

        //  The chunk after being used should be deallocated
        //  using standard 'free' function
        bool read (unsigned char **data_, size_t *size_)
        {
            unsigned char *chunk = (unsigned char*) malloc (chunk_size);
            assert (chunk);
            size_t pos = 0;

            while (true) {
                if (to_write) {
                    size_t to_copy = std::min (to_write, chunk_size - pos);
                    memcpy (chunk + pos, write_pos, to_copy);
                    pos += to_copy;
                    write_pos += to_copy;
                    to_write -= to_copy;
                }
                else {
                    if (!((static_cast <T*> (this)->*next) ()))
                    {
                        *data_ = chunk;
                        *size_ = pos;
                        return false;
                    }
                    continue;
                }
                if (pos == chunk_size)
                    break;
            }
            *data_ = chunk;
            *size_ = pos;
            return true;
        }

    protected:

        typedef bool (T::*parse_step_t) ();

        inline void next_step (void *write_pos_, size_t to_write_,
            parse_step_t next_)
        {
            write_pos = (unsigned char*) write_pos_;
            to_write = to_write_;
            next = next_;
        }

        inline bool fetch ()
        {
            //  TODO: review the deallocation of msg (w.r.t. zero copy)
            free_cmsg (msg);
            init_cmsg (msg);
            return proxy->read (source_thread_id, &msg);
        }

        cmsg_t msg;

    private:
        dispatcher_proxy_t *proxy;
        int source_thread_id;
        size_t chunk_size;
        unsigned char *write_pos;
        size_t to_write;
        parse_step_t next;
    };

}

#endif

使用make編譯源碼工程時,出現如下錯誤:

encoder.hpp:61:60: error: there are no arguments to 'memcpy' that depend on a template parameter, so a declaration of 'memcpy' must be available [-fpermissive]
                     memcpy (chunk + pos, write_pos, to_copy);
                                                            ^
encoder.hpp:61:60: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)

C++標準要求,對於依賴於模板參數的名字,將在模板類(或函數)實例化的時候進行名字查找,對於不依賴模板參數的名字,將搜索當前的定義. 舉例說明, 如下類定義:

template <typename T>
class C {
    T t;
    int get() {
        return t.getValue();
    }
};

成員函數get依賴於模板參數T,因此對於get的實現是否正確,只有在模板類C實例化並調用get函數的時候才知道.如果模板類C沒有實例化,這個類定義肯定是編譯正確的,因爲編譯器無從判斷模板參數T是否具有成員函數getValue.這個情況是典型的場景. 更需要注意的是具有繼承關係的場景,舉例如下:

template <typename T> class B {
    int f(){return 0;};
};

 template <typename T> class D : public B<T> {
    int get() { return f(); }
 };

這裏子類D的get方法調用父類中的方法f,而f是不依賴其模板參數的名字,編譯器將搜索f定義,而不是對應父類中的名字.所以編譯器會產生類似前文一樣的錯誤.爲了更清晰的表明子類使用父類中的成員函數,需要使用模板參數依賴的方法來訪問父類的成員函數.有2種形式可以消除這個錯誤,使用

int get() { return B<T>::f(); }

或者

int get() { return this->f(); }

當然,正如編譯器錯誤信息提示,可以使用-fpermissive 標誌讓編譯器接受這種代碼格式,但是這不是推薦的行爲.

回到開始ZeroMQ的編譯錯誤,函數memcpy沒有依賴類encoder_t的模板參數,但是不同的是,這裏不能用依賴模板參數的形式訪問memcpy,因爲memcpy是個庫函數,而不是模板類的成員函數,這裏的問題本質是沒要找到memcpy的定義,缺少了相應的頭文件string.h, 因此包含該頭文件即可.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章