はじめまして。
pianyiと申します。
下記のようなenumを宣言し、getter/setterを作ったのですが、getterでエラーが発生し
ます。
原因がさっぱり解りません。間違いや修正のヒントなど頂け無いでしょうか。
エラー内容:
error C2143: 構文エラー : ';' が 'CAiueoDlg::GetType' の前にありません。
ヘッダのソース:
class CAiueoDlg : public CDialog
public:
enum TYPE {
A,
B,
C,
D,
E,
F,
G
};
void SetType(TYPE tmp);
TYPE GetType();
private:
TYPE m_Type;
};
ソース:
#include AiueoDlg.h
TYPE CAiueoDlg::GetType()
{ return m_Type; }
void CAiueoDlg::SetType(TYPE type)
{ m_Type = type; }
GetType() の戻り値をintにするとビルドは通るのですが、
別クラスでGetした値がintになってしまうので、enumを使っている意味が無くなってしま
います。
Java や C# と同じ感覚で使用してはいけないのでしょうか?
よろしくお願いします。
開発環境:
OS:Windows XP SP3
ツール:VS 2008 VC++ MFC
単に、CAuieoDlgのクラス外の記述であるときに、TYPEが何かわからないから
エラーが出るだけのようです。
CAiueoDlg::TYPE CAiueoDlg::GetType()
{ return m_Type; }
void CAiueoDlg::SetType(CAiueoDlg::TYPE type)
{ m_Type = type; }
とすればイケると思います。
蒼の洞窟 さん
ご回答ありがとうございます。
確かに…
Getterの1行を「CAiueoDlg::TYPE CAiueoDlg::GetType()」に変更することによりエラー
は無くなりました。
「CAuieoDlgのクラス外の記述」という事は、ヘッダとソースファイルは、コンパイラ内
で別クラスと解釈されるって事なんでしょうか…
戻り値は中括弧の外だからグローバル解釈されるのかな…?
どちらにせよ、ありがとうございます。
本当に助かりました。m(_ _)m
以下愚痴です。
Setterには付け無くてもエラーにはならないのに…
ヘッダーにも書く必要が無いのに…
ひどい…(泣)
ん?
解決しちゃってますけど、明確に定義宣言すれば使えますよね。
class XXXX
{
public:
typedef enum TYPE {
A,B,C,D,E,F,G
}TYPE;
void SetType(TYPE ex_Type){ m_Type = ex_Type;}
TYPE GetType(){ return m_Type;}
private:
TYPE m_Type;
};
//----------------
XXXX x;
x.SetType( XXXX::D);
XXXX::TYPE t = x.GetType();
とか。
要は、中途半端な「タグ無し」がまずいだけのような気がするのですが(vv;)。
まぁ、厳密ではありますが、あんまり便利感がありせんけどね。
>>Setterには付け無くてもエラーにはならないのに…
これは、
>>CAiueoDlg::SetType
の段階で、CAiueoDlgのSetTypeというメンバ関数と限定されるので、
その後ろに来るTYPEはCAiueoDlgの型とみなされるんでしょう。
単純な、Set/Getならinlineにしちゃうとか。(仲澤@失業者さんの記述通り)
仲澤@失業者 さん
回答ありがとうございます。
> 解決しちゃってますけど、明確に定義宣言すれば使えますよね。
これは、inline で記述してるから出来てる気がしますが、違うのでしょうか?
試してみたのですがやはりエラーになります…
何が違うんだろう…orz
蒼の洞窟 さん
確かにinline にしてしまうのも手ですが、JavaやC#を長年やってるとソースが散らばる
のが嫌なんですよね…
特に今回は、getterは単純なんですが、setterは別処理をはさむのでinlineにはしたくな
いです。
ここまで来ると開発者や開発チームの規約になっちゃいますね。
え~と、TYPE型はclass XXXXの内部定義型なので、
「お外で使う場合も所有者の明示」が必要です。
class XXXX{
public:
typedef enum TYPE {
A,B,C,D,E,F,G
}TYPE;
// void SetType(TYPE ex_Type){ m_Type = ex_Type;}
// TYPE GetType(){ return m_Type;}
void SetType(TYPE ex_Type);
TYPE GetType();
private:
TYPE m_Type;
};
void XXXX::SetType(TYPE ex_Type){ m_Type = ex_Type;}
XXXX::TYPE XXXX::GetType(){ return m_Type;}
追記すると、
こういった、(厳密さは魅力だが)やや間抜けな記述になるので、
自分は可能な限り*.cppにコードは書きませんです(vv;)。
*.cppにコードすると「インライン展開されないことが確定」する
以外には、あまりメリットが見いだしづらいと考えます。
*.hに記述するメリットはいっぱいありますけどね(笑)。
なんだか文句たらたらのようだけど、言語仕様書に明記されている「仕様」だし
文句言うくらいなら(クラス内 enum を or C++ を)使わなきゃいい。
struct foo {
enum bar { ... };
bar value;
void set(bar x);
bar get() const;
}; // とあるとき
・作られる enum は foo::bar という名前となる (決して bar ではない)
・特定の文脈では foo を明示しない場合でも foo に属する識別子を先に見に行く
だけのこと。
後者の規則がないと foo のメンバーである識別子 value を探してくれないので不便。
void foo::set(bar x) { value=x; } // のような、
非インライン関数定義をコンパイラが処理する際の規則は、仕様書に定められている。
それによるとこの例では、開き括弧を読み取った時点で、
foo のメンバー関数 foo::set の定義であるとコンパイラが識別できる。そのため、
この開き括弧以後は foo の中の識別子を lookup に含める規則となっている。
なので bar の名前検索の際には foo:: を(自動的に)補うことができるため
上記 setter はコンパイルエラーにならない。
蒼の洞窟氏の指摘はそういうこと。
もちろん void foo::set(foo::bar x) { ... } と書いても問題ない。
一方で bar foo::get() const { return value; } という非インライン関数定義は
コンパイラが bar を読み取った時点ではまだ foo のメンバ関数定義と判断できない。
上記と同様、開き括弧を読み取った後であれば foo:: を補えるが、
それを既に読み取ったソースコード部分まで遡って適用しない規則。
なので 上記 getter の bar は不明な識別子であるエラーとなる。
foo::bar foo::get() const { return value; } なら OK.
> なんだか文句たらたらのようだけど、言語仕様書に明記されている「仕様」だし
> 文句言うくらいなら(クラス内 enum を or C++ を)使わなきゃいい。
横からすみませんが、
文句ではなく疑問ですよね。
疑問を持つことは理解するためのファーストステップであり、
それを批判するのはおかしいと思います。
仮に文句だったとしても、
誰も文句も言わず与えられた仕様に甘んじているだけでは
何の進歩もないでしょう。
#トピックと関係ない話で申し訳ありません。
仲澤@失業者 さん
なるほど!
*.h に全部書いちゃうって言うのも手ですね。
Javaとか、C#に近くなって良いかも…(やってみないと解らないけど…)
「ヘッダ = 実装なし」って言う固定概念が…
ん?*.cppファイルがいらない子になっちゃう(笑
tetrapod さん
ご回答ありがとうございます。
蒼の洞窟さんの補足&詳細説明ありがとうございます。
yukihiro さん
ありがとうございます。
トピックと関係ないですが、ありがとうございます。
皆さん、ありがとうございました。
順を追って説明すると、以下の通りです ^^
以下のように宣言すると、
class CAiueoDlg : public CDialog
public:
enum TYPE {
};
};
enumの型は言語仕様的に
CAiueoDlg::TYPE
です。
だから、
enum TYPE {
};
を直後に書いても、ちゃんと、グローバルスコープのenum とみなされ、
重複定義にならず、コンパイルが通ります。
(グローバルenumと、クラススコープ付きenumは違うつうことですね)
で、以下のコードがコンパイルエラーにならないのは、
class CAiueoDlg : public CDialog
public:
enum TYPE {
};
void SetType(TYPE tmp);
TYPE GetType();
};
SetType()関数が、CAiueoDlgクラスのメンバ関数であるから。
同一クラス内だから、「CAbcdeDlg::」の省略を許してくれているわけで、
ひどいどころか親切設計な訳です ^^
C++11 ならこう↓書けるす。
class Foo {
public:
enum TYPE { A,B,C,D,E,F,G };
void SetType(TYPE tmp);
TYPE GetType();
private:
TYPE m_Type;
};
auto Foo::GetType() -> TYPE { return m_Type; }
auto Foo::SetType(TYPE type) -> void { m_Type = type; }
bun さん
ご回答ありがとうございます。
>>ひどいどころか親切設計な訳です ^^
今回のルール?を知らない立場からすると、引数と戻り値の 両方省略可能 または 両方省略不可
とした方が、「統一性があって解りやすい。」と思った次第です。
なので、親切設計と言われると「小さな親切大きな…」と思いました。
確かにグローバル enum との兼ね合いがあるので、仕様設計者?も悩んだ結果なのでしょう。
しかし…
public の enum は、グローバル化してしまえば良いのか…?
private の enum なら、getter/setterなんて不要なんだし…
protected の enum が問題になるのか…
今回は使わないけど、悩ましい…
επιστημη さん
ご回答ありがとうございます。
C++11って、Visual Studio 2012?から組み込まれてる奴ですよね?
戻り値をautoにして、後ろに書くとは新しい発想ですね。
確かに、私の愚痴は解消されてますが、コーディング者泣かせですね(笑
いつの日かそれが当たり前になるのかなぁ。
> C++11って、Visual Studio 2012?から組み込まれてる奴ですよね?
このコード、VS2010(vc10)でも通るですよ。