web-dev-qa-db-ja.com

C ++エラー: ‘char *'の ‘char [2]への割り当てにおける互換性のない型

コンストラクターに少し問題があります。私のヘッダーファイルでは、次のように宣言します。

char short_name_[2]; 
  • およびその他の変数

私のコンストラクタでは:

Territory(std::string name, char short_name[2], Player* owner, char units);
void setShortName(char* short_name);
inline const char (&getShortName() const)[2] { return short_name_; }

私のcppファイル:

Territory::Territory(std::string name, char short_name[2], Player* owner, 
                     char units) : name_(name), short_name_(short_name), 
                    owner_(owner), units_(units)
{ }

私のエラー:

Territory.cpp:コンストラクター 'Territory :: Territory(std :: string、char *、Player *、char)':Territory.cpp:15:33:エラー: 'char *'から 'char [への割り当てで互換性のない型2] '

私はすでにそれを理解しましたchar[2] <=> char*ですが、コンストラクターとget/setterについてこれを処理する方法がわかりません。

9
vicR

C++のraw配列は煩わしく、危険に満ちています。これが、特に理由がない限り、std::vectorまたはstd::arrayを使用する必要がある理由です。

まず、他の人が言ったように、char[2]char*と同じではないか、少なくとも通常は異なります。 char[2]charのサイズ2の配列で、char*charへのポインターです。配列は必要なときにいつでも最初の要素へのポインタに減衰するため、混乱することがよくあります。したがって、これは機能します:

char foo[2];
char* bar = foo;

しかし、その逆ではありません。

const char* bar = "hello";
const char foo[6] = bar; // ERROR

混乱に加えて、関数パラメーターを宣言する場合、char[]char*と同等です。したがって、コンストラクタでは、パラメータchar short_name[2]は実際にはchar* short_nameです。

配列のもう1つの奇妙な点は、他の型のようにコピーできないことです(これは、関数パラメーターの配列がポインターとして扱われる理由の1つの説明です)。したがって、たとえば、私はできないこのようなことをすることができます:

char foo[2] = {'a', 'b'};
char bar[2] = foo;

代わりに、fooの要素を反復処理してbarにコピーするか、または std::copy などの機能を使用する必要があります。

char foo[2] = {'a', 'b'};
char bar[2];
// std::begin and std::end are only available in C++11
std::copy(std::begin(foo), std::end(foo), std::begin(bar));

したがって、コンストラクタでshort_nameの要素をshort_name_に手動でコピーする必要があります。

Territory::Territory(std::string name, char* short_name, Player* owner, 
                     char units) : name_(name), owner_(owner), units_(units)
{ 
    // Note that std::begin and std::end can *not* be used on pointers.
    std::copy(short_name, short_name + 2, std::begin(short_name));
}

ご覧のとおり、これは非常に煩わしいので、十分な理由がない限り、生の配列(またはこの場合はstd::vector)の代わりにstd::stringを使用する必要があります。

15
David Brown

関数が引数として配列を必要とする場合、代わりに配列の最初の要素へのポインターを取得します。このポインターは配列ではなくポインターであるため、配列の初期化には使用できません。

引数として配列にreferencesを受け入れる関数を記述できます。

void i_dont_accept_pointers(const char (array&)[2]) {}

ここでの問題は、この配列参照を使用して別の配列を初期化できないことです。

class Foo {
  char vars[2];
  Foo(const char (args&)[2])
    : vars(args)  // This will not work
  {}
};

C++ 11ではstd::arrayが導入され、配列のこの問題やその他の問題が解消されました。古いバージョンでは、配列要素を反復処理して個別にコピーするか、std::copyを使用する必要があります。

2
Oswald

C++は、Cのほとんどの規則を保持しています。

Cの場合は、配列を渡すために常にchar *を使用します。これは、Cが配列を参照するためです。関数に渡された場合でも、sizeof (short_name_)は8または4になります。これで、変数short_name_に2バイトのスペースができました。

コンストラクタがshort_name_に2バイトのメモリを割り当てました。そのバイトをそれにコピーするか、char *ポインタを使用して、サイズが2であると想定する必要があります。

第9章の「エキスパートCプログラミング:ディープCシークレット」を読んで理解してください。

簡単にするために、これはCスタイルのコードにすることができます。

#include <stdio.h>
#include <iostream>
using namespace std;
class Territory {
    private:
        string s;
        char c[2];
    public:
    Territory(std::string a, char b[2] /*visualize as char *b*/) {
        s  = a;
        c[0] = b[0]; //or use strcpy
        c[1] = b[1];
    }
    void PrintMe() {
        printf ("As string %s as char %c or %s\n",this->s.c_str(), c[0],c);
    }
};


main () {
    Territory a("hello", "h");
    a.PrintMe();
}
0
user12242622