Contents
構造体を packed 属性でぐしゃっとつぶして、alignment が崩れている (可能性がある)場合に、メンバ変数を引数を呼び出し先の 関数で、参照型でバインドしようとすると、コンパイラのエラーになる (g++-3.4.4, g++-4.1.1 で確認)。:
#include <iostream>
struct A {
int b;
} __attribute__ ((packed));
void func(int& val)
{
std::cout << "val = " << val << std::endl;
val *= 2;
}
int main()
{
A a;
a.b = 10;
//
// func(a.b);
//
// This code causes the compile error on g++-4.1.1.
//
// error: cannot bind packed field 'a.A::b' to 'int&'
//
int& x = a.b; func(x);
std::cout << "val = " << a.b << std::endl;
}
上のコードのコメント内の func(a.b) ではコンパイルが通らない。 で、int& x = a.b; func(x); として一旦呼び出し側で明示的に 一時的な参照型の変数に格納しておいてやる。これだとコンパイルが 通る。けど、これはコンパイラがやってくれても、いいじゃん。 Visual C++ はやってくれるよ。
今更ながら、char, short, unsigned int, long longなどの整数型を比較 する時のsigned/unsignedのミスマッチが気になる。:
short us = -1;
unsigned int i = 1;
if (us < i) {
std::cout << "Expected" << std::endl;
}
else {
std::cout << "Unexpected" << std::endl;
}
これで、コンパイル時に warning が出たりするけれど、たとえば warning が 何百とでて(C++ではありがち)とりあえずほっといたりすると、痛い目に 合う。
メンバ関数はconstでオーバーロードできるので、メンバ関数へのポインタだって 別だけど、どうやって書くの? こう。:
#include <iostream>
class
TestClass
{
public:
void print()
{
std::cout << "Non Const" << std::endl;
}
void print() const
{
std::cout << "Const" << std::endl;
}
};
int
main()
{
typedef void (TestClass::*memfun_t)();
typedef void (TestClass::*memfun_const_t)() const;
memfun_t memfun1 = &TestClass::print;
memfun_const_t memfun2 = &TestClass::print;
TestClass test;
(test.*memfun1)();
(test.*memfun2)();
}
try-catch で例外をちゃんと受け取らないと、プログラムは abort してしまうので、
とかいう関数でユーザ定義の処理にすることができる。これは libc の関数ではなく、C++ の言語仕様なので安心して 利用可能。
まとめ:
exception <----- logic_error <----- domain_error
<----- invalid_argument
<----- length_error
<----- out_of_range
<----- runtime_error <----- range_error
<----- overflow_error
代入演算子については、C++ の言語として他の算術演算子 * や == といった ものから区別され、大域関数ではオーバーロードできない。non static な メンバ関数でないといけない。
ユーザ定義の後置演算子 ++ は、前置演算子 ++ と区別するために:
Object & operator ++ () Object operator ++ (int)
として宣言される。int はダミーの引数なので使わない。 「プログラミング言語 C++」第3版 11.11参照。
class X; new X とかでオブジェクトの領域を確保して、初期化することは 基本中の基本だけど、あらかじめ割り当てておいた領域に新たなオブジェクト を作成することができる。 「プログラミング言語 C++」第3版 10.4.11参照。:
char buf[1024]; new (buf) X
勉強不足の STL はメモメモ。int 型の vector<int> のソートは、 例がたくさん見つかったけど、任意の構造体へのポインタ(vector<T*>) の場合は以下の通り。:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
template<typename T>
struct less_ptr: public ::std::binary_function<T *, T *, bool>
{
public:
bool operator () (const T * lhs, const T * rhs)
{
return *lhs < *rhs;
}
};
class A
{
int m_val;
public:
A(int val): m_val(val) {}
bool
operator < (const A & rhs) const
{ return m_val < rhs.m_val; }
int val() { return m_val; }
};
template<typename T>
bool
greater_ptr (T * lhs, T * rhs)
{
return ! ((*lhs) < (*rhs));
}
int
main()
{
typedef ::std::vector<A *> AList;
AList alist;
alist.push_back(new A(10));
alist.push_back(new A(1));
alist.push_back(new A(5));
alist.push_back(new A(3));
alist.push_back(new A(4));
// 関数ポインタアダプタを使ったソート
//
std::sort(alist.begin(), alist.end(),
std::ptr_fun(&greater_ptr<A>));
for (AList::iterator i = alist.begin(); i < alist.end(); ++i) {
std::cout << (**i).val() << " ";
}
std::cout << std::endl;
// 便利テンプレートをつくってみた
//
std::sort(alist.begin(), alist.end(), less_ptr<A>() );
for (AList::iterator i = alist.begin(); i < alist.end(); ++i) {
std::cout << (**i).val() << " ";
}
std::cout << std::endl;
}
二重字というのは、&&(and)、|(bitor)などの記号の別の表現方法。 (「プログラミング言語 C++」第3版 C.3.1参照)。ASCII の特殊文字の 位置に、キャラクタセットが割り当てられている場合の回避策として つかわれる。例えば、'&'は "<:"、'|'は":>"に置き換えられる。
これが以下のコンパイルエラーを引き起こす。:
template<
template <class> class ThreadingModel
>
class MultithredingClass
{
...
}
MultithreadingClass<::Loki::ClassLevelLockable> object;
<: が二重字として解釈され、意味不明のエラー出力を頂戴する。 最後の行を以下に変更。:
MultithreadingClass< ::Loki::ClassLevelLockable> object;
のようにスペースを一個いれておく。型名をマクロの引数に する場合にも気を付ける。
コンストラクタも一応テンプレートにできる模様(g++-2.95.3, g++-3.3)。 あたりまえと言えばあたりまえなのか?
任意のクラスを継承するときには、Class B みたいにパラメータの数だけ コンストラクタを書いておくと便利かも。:
#include <iostream>
#include <string>
class A {
public:
A() {}
A(const std::string & str): m_str(str), m_int(0) {}
A(const std::string & str, const int n): m_str(str), m_int(n) {}
void print_str () { std::cout << m_str << std::endl; }
void print_int () { std::cout << m_int << std::endl; }
private:
const std::string m_str;
const int m_int;
};
template<typename Base>
class B: public Base
{
public:
B () {}
template<typename P1>
B (P1 p1): A(p1) {}
template<typename P1, typename P2>
B (P1 p1, P2 p2): A(p1, p2) {}
};
int
main()
{
B<A> *b1 = new B<A>("Hello, world!!");
B<A> *b2 = new B<A>("Good-bye world!!", 10);
b1->print_str();
b2->print_int();
}
仮想関数のない継承関係の場合は、"4base" と表示される。:
#include <iostream>
#include <typeinfo>
using namespace std;
class base
{
public:
// virtual ~base() {}
};
class derived: public base
{
};
int
main()
{
base * p = new derived;
cout << typeid(*p).name() << endl;
}
ところが、仮想関数を実装すると、"7derived" と表示される。:
#include <iostream>
#include <typeinfo>
using namespace std;
class base
{
public:
virtual ~base() {}
};
class derived: public base
{
};
int
main()
{
base * p = new derived;
cout << typeid(*p).name() << endl;
}
「プログラミング言語C++ 第3版」の C.13.6 「限定子としてのtemplate」に 書いてあったことなんだけど、はまりまくったのでメモ。
以下のコード "T::print<U>()" の部分がコンパイルエラー。なぜならば コンパイラは T::print < U と解釈して、「小なり」演算子と勘違いする。 なので、コンパイラに「print は template なんだよ」と教えてやらなければ ならない。typename っていうのはあるが、template を限定子として使うとは しらなかった。Loki のソースは読むべし。
なので、"T::template print<U>()" に書き直せばよい。:
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
class PrintCout
{
public:
template<typename T>
void print()
{
cout << typeid(T).name() << endl;
}
};
class PrintCerr
{
public:
template<typename T>
void print()
{
cerr << typeid(T).name() << endl;
}
};
template<typename T>
class PrintTypename
{
public:
template<typename U>
void print()
{
T::print<U>();
}
};
int
main()
{
PrintTypename<PrintCout> obj;
obj.print<int>();
obj.print<char>();
obj.print<double>();
}
以下、書き直したソース:
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
class PrintCout
{
public:
template<typename T>
static void print()
{
cout << typeid(T).name() << endl;
}
};
class PrintCerr
{
public:
template<typename T>
static void print()
{
cerr << typeid(T).name() << endl;
}
};
template<typename T>
class PrintTypename
{
public:
template<typename U>
void print()
{
T::template print<U>();
}
};
int
main()
{
PrintTypename<PrintCout> obj;
obj.print<int>();
obj.print<char>();
obj.print<double>();
}