配列をポリモーフィックに扱ってはいけない。

873 名前: デフォルトの名無しさん 投稿日: 2007/09/23(日) 15:49:53

#include <stdio.h> 

class A { 
private: 
int a; 
public: 
A() { a = 10; } 
void put() { printf("%d\n", a); } 
virtual ~A() {} 
}; 

class B : public A { 
private: 
int b; 
public: 
virtual ~B() {} 
}; 

class D : public B {}; 

void a(A* d) 
{ 
for (int i = 0; i < 3; i++) { d[i].put(); } 
} 

int main(int argc, char* argv[]) 
{ 
D* d = new D[3]; 
a(d); 

delete [] d; 
return 0; 
} 

の時出力されるaの値は全て10を表示すると思ってるんですが・・・。
ならないのは何でなんでしょうか?
Visual Studio2005 SP1

878 名前: デフォルトの名無しさん 投稿日: 2007/09/23(日) 16:16:25
>>876
出力される値は
 10
 4290540
 -842150451
です。
875さんが言ってるとおりの動きをしてますね

875 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/23(日) 15:59:56
配列をポリモーフィックに扱ってはいけないから。

関数a()の中では、引数として渡された配列をA型の配列としてアクセスしているが、
実際はD型の配列なので、A::aとは違うところのメモリを読んで出力している可能性が高い。

877 名前: デフォルトの名無しさん 投稿日: 2007/09/23(日) 16:15:16
>>875
と言う事は配列ではなく1つ1つnewする必要があるってことですね?

879 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/23(日) 16:31:20
>>877 それもいいし、STLを使うとか、その場しのぎならD*でアクセスするとか

880 名前: デフォルトの名無しさん 投稿日: 2007/09/23(日) 16:35:25
>>879
STLではどうするんでしょうか?

今調べていたら、More Effectiveの項目3に書いてあるとネットでみたんですが
解決方法は規定クラスのポインタの配列で扱えば大丈夫ってわかりました。

881 名前: 879 [sage] 投稿日: 2007/09/23(日) 16:41:09
ポインタの配列をSTLで扱うことを考えてただけでした。そんなすごい方法じゃなくてごめん

882 名前: デフォルトの名無しさん 投稿日: 2007/09/23(日) 16:49:19
>>881
規定クラスのポインタの配列で試したらうまく動きました!!

ありがとうございました

なんともc++

671 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/20(木) 18:48:48
横からごめん

template void (U::*D::func)() = NULL;
ってどういう意味?
672 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/20(木) 19:04:46
>>671
単なる、staticなメンバー変数funcの定義だよ。
NULLで初期化してるだけ。

673 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/20(木) 19:05:53
void (U::*)() 型のstaticメンバ変数 D::func を定義して NULL で初期化している

700 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/20(木) 23:19:09
>>672
ぁーなるほど。
void (U::*)()の、D::funcを。ってことね。
了解しますた。さんくす

型Tから型Uへの変換が存在するかどうかを調べるtemplateの手法

155 名前: デフォルトの名無しさん [sage] 投稿日: 2007/07/30(月) 08:17:11
型Tから型Uへの変換が存在するかどうかを調べるtemplateの手法ってありますか?
具体的には次のようなことをやりたいのですが…

template<typename T> 
struct PtrWrapper { T* p; }; 

class A {...}; 
class B : public A {...}; 

PtrWrapper<A> a; 
PtrWrapper<B> b; 

a = b; // 可能 
b = a; // 不可能 

156 名前: デフォルトの名無しさん [sage] 投稿日: 2007/07/30(月) 11:44:19

template <typename T, typename U> 
class Conversion 
{ 
typedef char Small; 
class Big { char dummy[2]; }; 
static Small Test(U); 
static Big Test(...); 
static T MakeT(); 
public: 
enum { exists = sizeof(Test(MakeT())) == sizeof(Small) }; 
}; 

int main() { 
using namespace std; 
cout 
<< Conversion<double, int>::exists << endl 
<< Conversion<char, char*>::exists << endl 
<< Conversion<size_t, vector<int> >::exists << endl 
<< Conversion<A*, B*>::exists << endl 
<< Conversion<B*, A*>::exists << endl; 
} 

詳しくは Modern C++ Design 参照。

160 名前: デフォルトの名無しさん [sage] 投稿日: 2007/07/31(火) 00:30:10
>>156
ありがとう!
ていうか解説読んで感動しました…

161 名前: デフォルトの名無しさん [sage] 投稿日: 2007/07/31(火) 09:22:22

>160
どうせ中では同じようなことをやってるはずだけど、使えるなら Boost TypeTraits を使うのが楽。この場合は is_convertible

c++スレより 「クラスの内側で定義したクラステンプレートを特殊化する」

何の気なくのぞいてみたら、2chって宝の山なのですね。
過去スレとかにも面白い書き込みがたくさんあり、幾つかがdatに行く前にこうやって張れたらと思います。

91 名前: デフォルトの名無しさん 投稿日: 2007/09/30(日) 20:19:13
クラスの内側で定義したクラステンプレートを特殊化する、

class X { 
  template<typename T> class Y {}; 
  template<> class Y<int> {}; 
}; 

のようなコードは、VC++(2003/2005)ではコンパイルできますが、g++3/4では
error: explicit specialization in non-namespace scope ‘class X’ というエラー
になってしまいます。

YをXの中ではなく、名前空間スコープで
template<> class X::Y {};
と書けばg++でもVC++でも問題なく特殊化できることは知っているのですが、
なんとかg++で、特殊化されたクラスの定義をXの中に書く方法はないでしょうか?

YとYの定義が離れた場所にあると、コードが読みにくいと思うのです。

boost(特にmpl)の使用は歓迎です。
よろしくおねがいします。
 
93 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 20:42:48
>>91
定義位置を近づけたいなら、両方クラス外に書けばいいんじゃね?

94 名前: 91 投稿日: 2007/09/30(日) 20:45:33 >>93
ああ、たしかにそうですね。他に案がなければそうしたいとおもいます。

ただ、YとYを両方クラス内に書けると、宣言と定義の位置も離れない(定義だけになる)ので、
より読みやすいと思っています。

というわけで、引き続きお願いします。

 
95 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 20:54:22
>>91
規格ではネストされたクラス内での明示的特殊化は禁止されているけど
部分特殊化はOKなので、

class X { 
  template <typename T, class U = void> class Y {}; 
  template <typename T> 
  class Y< T 
      , typename boost::enable_if< 
         typename boost::is_same< T, int> 
       > > {}; 
}; 

のようにenable_ifとis_sameを使って書くといいっぽい(初心者スレから一部拝借w)
VCでコンパイル可能なのはmsの独自拡張でgccの方が正しいとのこと

 
96 名前: 91 投稿日: 2007/09/30(日) 20:57:38
なお、メンバ関数テンプレートで同様のエラーをくらう件については
(下記の2行めがエラーになる)、

class X { 
  template<typename T> void foo(T x) {} 
  template<> void foo<int>(int x) {} // error 
}; 

下記方法でごまかしています。

#include <boost/type.hpp> 
class X { 
  void foo_(int x, boost::type<int>) { /* specialized */ } 
  template<typename T> void foo_(T x, ...) {} 
public: 
  template<typename T> void foo(T x) { foo_(x, boost::type<T>()) ;} 
}; 

 
97 名前: 91 [sage] 投稿日: 2007/09/30(日) 20:59:12
>>95
部分特殊化はOKだったんですか。気づきませんでした。
早速試してみます。どうもありがとう〜!!
 
98 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 21:00:46
>>91
私はC++初心者ですが、>>93さんと同意見です。
>>95さんの言うようにgccの方が正しい(つまり規格どおりということですよね)ならば、
なおさらそう思います。

それに、Xクラスの定義を見たいときには、むしろYは外にあった方が見やすいのではないでしょうか?
 
99 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 21:06:58
ちなみに関数テンプレでもこの手は使えるようだ
この手法も名前があったと思うけど思い出せない
使うなら適当にローカルなメタ関数を専用の名前空間に自作して階層を浅くする工夫が必要だね
使ってればわかるけど、凄く見にくくなるから
 
100 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 21:10:12
あ、::type付けわすれた…
 
102 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 22:00:44
>>98 >>101
読みやすいかどうかは読み手にもよるし、あまり>>91や>>94はよい聞きかたではなかったですね。
C++のコードを自動生成するツール(自作)の都合でX内に定義を書けると嬉しい、というのが実際の事情です。
 
103 名前: 91 [sage] 投稿日: 2007/09/30(日) 22:32:03
102は91です。

>>99
concept-controlled polymorphism ですか?
  
104 名前: デフォルトの名無しさん 投稿日: 2007/09/30(日) 23:06:47
>>100
> あ、::type付けわすれた…
検索エンジン経由で来るひとのために、一応訂正版貼っときますね。

#include <cstdio> 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits/is_same.hpp> 

struct X { 
  template<typename T, typename U = void> struct Y { 
    Y() { std::printf("genecic\n"); } 
  }; 
  template<typename T> struct Y <T, typename boost::enable_if<boost::is_same<T, int> >::type> { 
    Y() { std::printf("specialized for int\n"); } 
  }; 
}; 
class Z {}; 

int main() { 
  X::Y<Z> a; 
  X::Y<int> b; 
  X::Y<float> c; 
} 

実行結果は、
genecic
specialized for int
genecic
です。
 
105 名前: 91=104 [sage] 投稿日: 2007/09/30(日) 23:31:30
boostが使えない場合は、VC++2003/2005/g++対応だけ考えるならこんな感じでしょうか。

#include <cstdio> 
namespace b00st { 
  template <bool B, class T = void> 
  struct enable_if_c { 
    typedef T type; 
  }; 
  template <class T> 
  struct enable_if_c<false, T> { /* NO TYPEDEF! */ }; 
  template <class Cond, class T = void> 
  struct enable_if : public enable_if_c<Cond::value, T> {}; 
  template<bool b> struct bool_ { 
    static const bool value = b; 
  }; 
  template<typename T, typename U> 
  struct is_same : bool_<false> {}; 
  template<typename T> 
  struct is_same<T, T> : bool_<true> {}; 
} 
using namespace b00st; 
struct X { 
  template<typename T, typename U = void> struct Y { 
    Y() { std::printf("genecic\n"); } 
  }; 
  template<typename T> struct Y<T, typename enable_if<is_same<T, int> >::type> { 
    Y() { std::printf("specialized for int\n"); } 
  }; 
}; 

そろそろウザいとおもうので、これで打ち止めにします。ありがとうございました。
 
106 名前: デフォルトの名無しさん [sage] 投稿日: 2007/09/30(日) 23:40:10
このtemplate感、実に小気味良い 久しぶりにC++スレを実感した

c++でStrategyパターンってこんな感じ? 紆余曲折の結果

algorithmのインターフェースを設定しているクラスにstatic関数がいてもいいのかとても悩みながら。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4     
  5 #include <iostream>
  6 #include <string>
  7 #include <map>
  8 #include <stdexcept>
  9 #include <exception>
 10 #include "openssl/md5.h" 
 11 #include "openssl/sha.h"
 12 
 13 typedef unsigned int u_int;
 14         
 15 using std::cout;
 16 using std::cerr;
 17 using std::endl;
 18 using std::string;
 19 using std::logic_error;
 20 
 21 struct HashPtn
 22 {   
 23     virtual string get_hash( const string s ) const = 0;
 24         
 25     // 定義だけするはずのクラスでこんなことしてよいのだろうか?
 26     static char *pt(unsigned char *md, int len)
 27     {   
 28         int i; 
 29         // マジックナンバー、どうしたものか。
 30         static char buf[80];
 31         
 32         for (i=0; i<len; i++)
 33             sprintf(&(buf[i*2]),"%02x",md[i]);
 34         return(buf);
 35     }
 36 
 37 };  
 38     
 39 struct MD5_hash : public HashPtn 
 40 {       
 41     virtual string get_hash ( const string s ) const 
 42     {
 43         unsigned char digest[MD5_DIGEST_LENGTH];
 44         MD5_CTX ctx;
 45 
 46         MD5_Init(&ctx);
 47         MD5_Update(&ctx, reinterpret_cast<const unsigned char *>(s.c_str()), s.length());
 48         MD5_Final(digest, &ctx);
 49 
 50         string ret = reinterpret_cast<char *>(HashPtn::pt(digest, MD5_DIGEST_LENGTH));
 51         return ret;
 52     }
 53 };
 54 
 55 
 56 struct SHA1_hash : public HashPtn 
 57 {
 58     virtual string get_hash ( const string s ) const 
 59     {
 60         unsigned char digest[SHA_DIGEST_LENGTH];
 61         SHA_CTX ctx;
 62 
 63         SHA1_Init(&ctx);
 64         SHA1_Update(&ctx, reinterpret_cast<const unsigned char *>(s.c_str()), s.length());
 65         SHA1_Final(digest, &ctx);
 66 
 67         string ret = reinterpret_cast<char *>(HashPtn::pt(digest, SHA_DIGEST_LENGTH));
 68         return ret;
 69     }
 70 };
 71 
 72 struct Hash
 73 {
 74     HashPtn * m_strategy ;
 75     Hash( HashPtn * in_strategy )
 76     {
 77         m_strategy = in_strategy ;
 78     }
 79 
 80     string get_hash_( string s ) const 
 81     {
 82         return m_strategy->get_hash( s.c_str() );
 83     }
 84 } ;
 85 
 86 Hash get_algorithms_map( const string my_algorithm )
 87 {
 88     typedef map<string, Hash> algorithms_map_t;
 89     algorithms_map_t algorithms_map;
 90 
 91     MD5_hash md5;
 92     SHA1_hash sha1;
 93     Hash md5_h( &md5 );
 94     Hash sha1_h( &sha1 );
 95 
 96     algorithms_map.insert( pair <string, Hash>( "md5" , md5_h  ) );
 97     algorithms_map.insert( pair <string, Hash>( "sha1", sha1_h ) );
 98 
 99     algorithms_map_t::iterator p;
100     p = algorithms_map.find( my_algorithm );
101     if ( p != algorithms_map.end())
102     {
103         return (*p).second;
104     }
105     else 
106     {
107         throw logic_error("not found");
108     }
109 }
110 
111 int main ()
112 {
113     // 本当は引数とかでこの値を入れると思います。
114     string my_algorithm = "md5";
115     Hash h = get_algorithms_map( my_algorithm );
116     cerr << h.get_hash_("hoge") << endl;
117 
118     return 0;
119 }

c++でStrategyパターンってこんな感じ? constを意識してみる。

#include <iostream>
#include <string>
#include <map>
#include <stdexcept>
#include <exception>
#include "openssl/md5.h"
#include "openssl/sha.h"

typedef unsigned int u_int;

using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::logic_error;

struct HashPtn
{struct SHA1_hash : public HashPtn 
{   
    // const1 引数を変更しないconst
    // const2 この関数を使うことによってオブジェクトに何の変更も起きないことを指定するconst
    virtual string get_hash ( const string s ) const 
    {
        unsigned char digest[SHA_DIGEST_LENGTH];
        SHA_CTX ctx;

        SHA1_Init(&ctx);
        SHA1_Update(&ctx, reinterpret_cast<const unsigned char *>(s.c_str()), s.length());
        SHA1_Final(digest, &ctx);

        string ret = reinterpret_cast<char *>(digest);
        return ret;
    }
};

struct Hash
{
    HashPtn * m_strategy ;
    Hash( HashPtn * in_strategy )
    {
        m_strategy = in_strategy ;
    }

    string get_hash_( string s ) const 
    {
        return m_strategy->get_hash( s );
    }
} ;

Hash get_algorithms_map( const string my_algorithm )
{   
    typedef map<string, Hash> algorithms_map_t;
    algorithms_map_t algorithms_map;
    
    MD5_hash md5;
    SHA1_hash sha1;
    Hash md5_h( &md5 );
    Hash sha1_h( &sha1 );

    algorithms_map.insert( pair <string, Hash>( "md5" , md5_h  ) );
    algorithms_map.insert( pair <string, Hash>( "sha1", sha1_h ) );
    
    algorithms_map_t::iterator p;
    p = algorithms_map.find( my_algorithm );
    if ( p != algorithms_map.end())
    {
        return (*p).second;
    }   
    else 
    {
        throw logic_error("not found");
    }
}

    
int main ()
{   
    // 本当は引数とかでこの値を入れると思います。
    string my_algorithm = "md5";
    Hash h = get_algorithms_map( my_algorithm );                                                      
    cerr << h.get_hash_("hoge") << endl;
    return 0;
} 

    virtual string get_hash( const string s ) const = 0;
};  
    
struct MD5_hash : public HashPtn 
{   
    virtual string get_hash ( const string s ) const 
    {
        unsigned char digest[MD5_DIGEST_LENGTH];
        MD5_CTX ctx;
        MD5_Init(&ctx);
        MD5_Update(&ctx, reinterpret_cast<const unsigned char *>(s.c_str()), s.length());
        MD5_Final(digest, &ctx);

        string ret = reinterpret_cast<char *>(digest);
        return ret;
    }
};

c++でStrategyパターンってこんな感じ? の小修正

main部分をもう少し手直ししてみた。しかしこれだと次にsha128の実装を行うときに3行も追加が必要となる。なるべくであれば追加は一行だけでうまくまわしたいものなのだけど、まだその手法がよく分からない。

 68 Hash get_algorithms_map( string my_algorithm )
 69 {                                             
 70     typedef map<string, Hash> algorithms_map_t;
 71     algorithms_map_t algorithms_map;
 72                 
 73     MD5_hash md5;
 74     SHA1_hash sha1;
 75     Hash md5_h( &md5 ); 
 76     Hash sha1_h( &sha1 );
 77 
 78     algorithms_map.insert( pair <string, Hash>( "md5" , md5_h  ) );
 79     algorithms_map.insert( pair <string, Hash>( "sha1", sha1_h ) );
 80 
 81     algorithms_map_t::iterator p;          
 82     p = algorithms_map.find( my_algorithm );
 83     if ( p != algorithms_map.end())
 84     {                     
 85         return (*p).second;
 86     }
 87     else 
 88     {                                 
 89         throw logic_error("not found");
 90     }
 91 }
 92 
 93           
 94 int main ()
 95 {
 96     // 本当は引数とかでこの値を入れると思います。
 97     string my_algorithm = "md5";               
 98     Hash h = get_algorithms_map( my_algorithm );
 99     cerr << h.get_hash_("hoge") << endl;
100     return 0;
101 }