最近在編譯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
, 因此包含該頭文件即可.