void f(int) {puts("f:int");}
void f(char) {puts("f:char");}
f(1);
f('1');
---
f:int
f:char
class Cを適当に作ってCとcharを引数に持つ関数を作ってももちろん呼び分けられる.
struct C {};
void g(char) {puts("g:char");}
void g(const C&) {puts("g:C");}
g(1);
g(C());
---
g:char
g:C
intかCへの変換オペレータを持つ型を作ってやり, その型を返しても大丈夫.
struct Type {
operator int() { return 3; }
operator C() { return C(); }
};
Type getValue() { return Type(); }
int a = getValue(); // (***)
C c = getValue(); // (***)
int a; a = getValue();もOK. しかし
C c; c = getValue(); // VCはOK, gccはNGだとgcc 4.4.0ではambiguousとなりコンパイルエラー(VC9SP1はOK). ついでに上記g()に渡してもエラー(VC9SP1だとOK).
g(getValue()); // VCはOK, gccはNGこの挙動の差はどちらが正しいのだろう. 最初に示したようにcharとintを区別するならVCのようにgetValue()でも判断してくれてもいいように思う. (***)はgccでもOKなんだし. 実際にはCはstd::stringとしてやりたいんだけど, うまくgccをまるめこむ方法はあるんだろうか.
char t[] = "c++";
char q[] = "c++";
std::cout << equal(t, q) << std::endl;
がfalseになるので例としてよろしくない.
==20640== Invalid read of size 4 ==20640== at 0x8053522: boost::unit_test::framework_impl::clear()というような警告が出る(32bit/64bit両方.最適化オプションに関係ない). いろいろ試したら framework_impl::clearの
if( test_id_2_unit_type( tu.second->p_id ) == tut_suite )
delete (test_suite const*)tu.second;
else
delete (test_case const*)tu.second;
を
if( test_id_2_unit_type( tu.second->p_id ) == tut_suite )
delete (test_suite const*)tu.second;
else {
const test_case *p = (test_case const*)tu.second;
delete p;
}
のように中間変数に代入してから削除すると大丈夫になった.
なんで挙動が異なるねんと思ったが, 実際asm出力を見ても該当する個所で~test_case()を呼び出してからdelete(void*)を呼び出す前に前者だと開放領域にメモリアクセスが一度入るように見える(後者はレジスタに残してる).
なんとなく雰囲気は分かるんだけど完全には理解できてない. 簡単なサンプルを書こうとしたけどうまく再現できなかった.
どなたか二つの違いが明確になるサンプル教えてくださいまし.
てかboost.Test巨大すぎて嫌い. この件も何度か指摘されてる
(1,
2
)みたいだし. もう使わない.
#define TEST_EQUAL(x, y) if ((x) == (y)) ...みたいなのをつくったのだけど, これではC文字列の比較が意図したものと異なる. で
template<typename T, typename U>
bool compare(const T& lhs, const U& rhs)
{
return lhs == rhs;
}
を使って
#define TEST_EQUAL(x, y) if (compare(x, y)) ...にしてconst char*版の特殊化を用意しようとして少しはまった. 上の特殊化は
template<>
bool compare(char *lhs, char *rhs)
{
return strcmp(lhs, rhs) == 0;
}
じゃなくて
template<> bool compare(const char *const &lhs, const char *const &rhs);なんだな. これだと本当にconst char*なものにしかマッチしない. char buf[4]; みたいなのはアウト. で結局特殊化じゃなくてオーバーロードを4つ用意した.
bool compare(const char *lhs, const char *rhs); bool compare(char *lhs, const char *rhs); bool compare(const char *lhs, char *rhs); bool compare(char *lhs, char *rhs);
const char str1[] = "abc";
char str2[] = "abc";
char *str3 = "abc";
const char *str4 = "abc";
puts("1");
compare(str1, str1);
compare(str1, str2);
compare(str1, str3);
compare(str1, str4);
puts("2");
compare(str2, str1);
compare(str2, str2);
compare(str2, str3);
compare(str2, str4);
puts("3");
compare(str3, str1);
compare(str3, str2);
compare(str3, str3);
compare(str3, str4);
puts("4");
compare(str4, str1);
compare(str4, str2);
compare(str4, str3);
compare(str4, str4);
というテストコードでそれぞれどれにマッチするかみたらVCとgccで微妙に違うんだなあ.
gcc vc
1 1 1 1 1 1 1 1
ok ok
ok ok ok
ok ok
ok ok
2 2 2 2 2 2 2 2
ok ok ok ok
ok ok ok ok ok
ok ok
ok ok ok
3 3 3 3 3 3 3 3
ok ok
ok ok ok
ok ok
ok ok
4 4 4 4 4 4 4 4
ok ok
ok ok ok
ok ok
ok ok
どちらも4パターン必要なことには変わりないんだが, ややこしい.
-- a.h --
#ifndef A_H_
#define A_H_
enum Type {
typeB,
typeC
};
/* 基底クラス */
struct A {
A(int type) : type_(type) {}
const int type_;
int func(int n) const;
};
#endif
--- b.h ---
#include "a.h"
struct B : public A {
B() : A(typeB) {}
int func(int n) const { return n * 2; }
};
--- c.h ---
#include "a.h"
struct C : public A {
C() : A(typeC) {}
int func(int n) const { return n + 1; }
};
--- a.cpp ---
#include "a.h"
#include "b.h"
#include "c.h"
/* 仮想関数もどき */
int A::func(int n) const
{
switch (type_) {
case typeB:
return static_cast<const B*>(this)->func(n);
case typeC:
return static_cast<const C*>(this)->func(n);
default:
__assume(0);
}
}
--- t.cpp ---
#include <stdio.h>
#include "b.h"
#include "c.h"
int main()
{
B b;
C c;
A* a = &b;
A* aa = &c;
printf("sum=%d\n", a->func(10) + aa->func(2));
}
という仮想関数もどきの関数funcを作ってみたら
push 23 ; 計算済み push OFFSET "sum=%d" call DWORD PTR __imp__printf add esp, 8とまで最適化されてしまった(ちょっと頭おかしいんじゃないかという気もするが). それだったらvirtualでも直接呼び出しするぐらいできるだろうに.
struct A {
virtual void func() { }
};
struct B : public A {
void func() { puts("a"); }
};
int main()
{
B b;
A *a = &b;
a->func();
}
なんてコードはどうみてもputsを呼べばいいように見えるんだが, いちいちvtblを見に行く.
__declspec(novtable)なんてのが10年前からあるだからちょっとでもけちろうという考えはあるし, 昨今の最適化能力を見てると難しくはなさそうなんだけど不思議だ.
一筋縄ではいかない理由があるんだろうなあ.
コンパイル済みバイナリ(プログラム)を起動するとCPUが処理中の状態になり, しばらくすると終了する(あるいはいつまでたっても終わらない)
というのは一般的なので未定義用語として使えると思う(あえて「実行」を避けた). デバッガを使うとプログラムを変更しなくても処理中の状態を任意の場所で一時停止, 再開できる. ステップ実行は, ソースコードの1行に対応する処理が終わるごとに自動的に一時停止し, 再開と一時停止を繰り返す手続きと言えばよさそうだ. 1行に関数を含む場合, nextはその関数を処理したあと次の行に移る前に一時停止, stepはその関数の実装コードの中の1行目で一時停止と言えばどうだろう. 本ではなんでもかんでも「実行」と書いてあるから混乱を招きかねない. たとえば次の箇所は使い分けられる. むしろ実行を使わない方がいいのかも.