ページ 1 / 1
VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 15:59
by sereparu
VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにする方法はあるでしょうか?
例えば、あるクラス(TestClass)の静的メンバ変数(member)の値を返すgetter関数を呼び出す際に
「TestClass::getMember()」のように括弧ありで呼び出すと、正常にメンバ変数の値が返りますが
「TestClass::getMember」のように括弧なしで呼び出すと、異常な値(「001513BB」などの16進数(?)のような値)が返ります。
括弧なしで呼び出したとしても、返った値を変数に代入したり、別の値と比較したりする場合は
型の不一致でエラーになるので気付くことができます。
しかし、返った値を0と比較する場合など、エラーにならない場合もあります。
(下記コード参照)
このような場合だと、括弧の付け忘れに気付かず、バグの原因になってしまうので
どんな場合でも、括弧なしで関数を呼び出した場合はコンパイルエラーにしたいです。
VisualStudioを使っているので、設定でエラーにできないか確認しましたが、そのような設定は見当たりませんでした。
設定でエラーにできない場合、括弧の付け忘れを防止できるようなコードの書き方があれば、ご教授願います。
よろしくお願い致します。
また、括弧なしの関数の呼び出しは、どのような場合に使用するのでしょうか?
こちらも併せてご回答いただけると幸いです。
開発環境は以下の通りです。
OS:Windows7(64bit) Home Premium SP1
統合開発環境:Visual Studio Community 2017
コンパイラ:Visual C++ 2017
ライブラリ:DXライブラリVer3.18a
コード:
#include "stdafx.h"
#include <iostream>
/**
* テスト用クラス
*/
class TestClass
{
private:
static int member; // メンバ変数
public:
static int getMember(void); // メンバ変数ゲッター
};
int TestClass::member = 0;
/**
* メンバ変数ゲッター
*
* @return member メンバ変数
*/
int TestClass::getMember(void) {
return member;
}
int main(int argc, char *argv[]) {
// 括弧あり = 正常な値(0)
std::cout << "getMember() = " << TestClass::getMember() << "\n";
// 括弧なし = 異常な値(001513BBなど)
std::cout << "getMember = " << TestClass::getMember << "\n";
// 括弧なしの場合、メンバ変数は0なのに、0と比較しても真にならない
// また、コンパイルエラーにならない
if (0 == TestClass::getMember) {
std::cout << "TRUE";
}
getchar(); // 入力待ち
return 0;
}
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 16:42
by みけCAT
sereparu さんが書きました:例えば、あるクラス(TestClass)の静的メンバ変数(member)の値を返すgetter関数を呼び出す際に
「TestClass::getMember()」のように括弧ありで呼び出すと、正常にメンバ変数の値が返りますが
「TestClass::getMember」のように括弧なしで呼び出すと、異常な値(「001513BB」などの16進数(?)のような値)が返ります。
後者は関数を呼び出しているのではなく、関数のアドレスを得ているだけです。
sereparu さんが書きました:また、括弧なしの関数の呼び出しは、どのような場合に使用するのでしょうか?
括弧なしの関数の呼び出しは、括弧を使わなくても関数を呼び出せる言語を使う場合に使用します。
また、C++においても、演算子のオーバーロードを用いると、括弧を用いずに関数を呼び出せます。
関数を呼び出さずにアドレスを取得するのは、関数ポインタ変数に関数のアドレスを代入して処理を切り替える場合などに使用します。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 18:09
by sereparu
みけCATさん、迅速なご回答誠にありがとうございます。
みけCAT さんが書きました:
後者は関数を呼び出しているのではなく、関数のアドレスを得ているだけです。
括弧なしの呼び出しだと全く別の処理になってしまうんですね。
知らなかったので確認できて良かったです。
みけCAT さんが書きました:
また、C++においても、演算子のオーバーロードを用いると、括弧を用いずに関数を呼び出せます。
「括弧を用いずに関数を呼び出せる」というのは
「TestClass::getMember」と書いても「TestClass::getMember()」と
同じ結果が得られるようになる、という意味でしょうか?
演算子のオーバーロードは使ったことがないので、方法が分かりません。
演算子のオーバーロードについて調べて、自分なりに考えてみました。
例えば、上記コードの例だと、TestClassに
コード:
int operator +() {
return getMember();
}
というように演算子のオーバーロードを追加して
TestClassのオブジェクト(obj)を作って、「+obj」とすればメンバ変数の値を得ることができました。
この方法なら、確かに括弧を用いずに関数を呼び出せる、ということになりますが
当初の目的とは異なってしまいます。
あくまで目的は、関数呼び出し時に末尾の括弧を付け忘れても意図しない動作にならないよう
括弧の付け忘れに気付けるようにするか、括弧を付けた場合と同じ動作にできるようにすることです。
もし、演算子のオーバーロードを用いれば、「TestClass::getMember」と書いても
「TestClass::getMember()」と同じ結果が得られるようになる、という意味でご回答いただいたのでしたら
よろしければ、上記コードの例を用いた具体的な方法をご回答いただけますでしょうか?
よろしくお願い致します。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 18:31
by みけCAT
sereparu さんが書きました:「括弧を用いずに関数を呼び出せる」というのは
「TestClass::getMember」と書いても「TestClass::getMember()」と
同じ結果が得られるようになる、という意味でしょうか?
いいえ、そのような意図ではありません。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 22:02
by Math
コード:
#include "stdafx.h"
#include <iostream>
/**
* テスト用クラス
*/
class TestClass
{
private:
static int member; // メンバ変数
public:
static int getMember(void); // メンバ変数ゲッター
};
int TestClass::member = 0;
/**
* メンバ変数ゲッター
*
* @return member メンバ変数
*/
int TestClass::getMember(void) {
return member;
}
int(*func_p)(void);/////関数ポインター func_p
int main(int argc, char *argv[]) {
// 括弧あり = 正常な値(0)
std::cout << "getMember() = " << TestClass::getMember() << "\n";
// 括弧なし = 異常な値(001513BBなど)
std::cout << "getMember = " << TestClass::getMember << "\n";
// 括弧なしの場合、メンバ変数は0なのに、0と比較しても真にならない
// また、コンパイルエラーにならない
if (0 == TestClass::getMember) {
std::cout << "TRUE";
}
func_p = TestClass::getMember;/////関数ポインター func_p へ 関数TestClass::getMemberのポインターの代入
std::cout << "getMember = " << func_p() << "\n";
getchar(); // 入力待ち
return 0;
}
ビルド
コード:
1>------ すべてのリビルド開始: プロジェクト:ConsoleApplication1, 構成: Debug Win32 ------
1>c1.cpp
1>ConsoleApplication1.vcxproj -> D:\c\2017\cpp\001\01\ConsoleApplication1\Debug\ConsoleApplication1.exe
1>ConsoleApplication1.vcxproj -> D:\c\2017\cpp\001\01\ConsoleApplication1\Debug\ConsoleApplication1.pdb (Partial PDB)
========== すべてリビルド: 1 正常終了、0 失敗、0 スキップ ==========
実行結果
コード:
getMember() = 0
getMember = 00D313A7
getMember = 0
のようにTestClass::getMemberは関数ポインターでありエラーにはできませんね。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月20日(日) 23:29
by sereparu
Mathさん、ご回答ありがとうございます。
関数ポインターは使ったことがないですが、そういう使い方があるんですね。
エラーにならない以上、やっぱり括弧を付けるように気をつけるしかないでしょうか。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 00:14
by Math
その通りですよ。(^^;
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 00:54
by inemaru
「関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい」 ではなくて、
「想定外の呼び出しをエラーにする」に妥協するならば、
エラーを出すことは可能ですが、どうでしょう?
コード:
#include <iostream> // 標準IO
#include <utility> // std::forward
class TestClass final {
private:
static int member;
public:
static int getMember() {
return member;
}
static int getValue(int v) {
return v;
}
static int getAdd(int l, int r) {
return l + r;
}
};
int TestClass::member = 0;
// 関数を呼ぶこと明示するメタ関数
// safe_call( 関数名, 引数(可変) );
template <class Func, class... Args>
auto safe_call(Func&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
return f(std::forward<Args>(args)...);
}
int main(int argc, char *argv[])
{
// 関数を呼ぶことを明示する場合は、safe_callを使って呼び出す
std::cout << safe_call(TestClass::getMember) << std::endl; // 引数なし
std::cout << safe_call(TestClass::getValue, 100) << std::endl; // 引数あり
std::cout << safe_call(TestClass::getAdd, 100, 100) << std::endl; // 引数あり(複数)
// safe_callを使って呼び出す時は、()をつけるとエラー
// (エラーなので、コンパイルせずとも Visual Studio のエディタ上に赤線が出る)
//std::cout << safe_call(TestClass::getMember()) << std::endl; // 引数なし
//std::cout << safe_call(TestClass::getValue(100), 100) << std::endl; // 引数あり
// 入力待ち
rewind(stdin), getchar();
return 0;
}
とは言え、括弧を付けるように注意した方が手っ取り早いですね。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 05:29
by かずま
getMember を関数オブジェクトにすると、
TestClass::getMember がエラーになり、
TestClass::getMember() が期待通り、member を取得するものとなります。
コード:
#include "stdafx.h"
#include <iostream>
/**
* テスト用クラス
*/
class TestClass
{
private:
static int member; // メンバ変数
public:
static struct T { int operator()(); } getMember; // 関数オブジェクト
};
int TestClass::member = 0;
TestClass::T TestClass::getMember;
/**
* メンバ変数ゲッター
*
* @return member メンバ変数
*/
int TestClass::T::operator()() {
return member;
}
int main(int argc, char *argv[]) {
// 括弧あり = 正常な値(0)
std::cout << "getMember() = " << TestClass::getMember() << "\n";
// 括弧なし = 異常な値(001513BBなど)
std::cout << "getMember = " << TestClass::getMember << "\n";
// 括弧なしの場合、メンバ変数は0なのに、0と比較しても真にならない
// また、コンパイルエラーにならない
if (0 == TestClass::getMember) {
std::cout << "TRUE";
}
getchar(); // 入力待ち
return 0;
}
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 13:04
by かずま
かずま さんが書きました:
TestClass::getMember がエラーになり、
もっと正確にいうと、int val = 3; という宣言の下で、
val; という文を書いてもエラーにならないのと同様に、
TestClass::getMember; と書いてもエラーにはなりません。
std::cout << TestClass::getMember; がエラーになるのは、
<<演算子のオペランド(ostream::operator<< の引数) が
TestClass::T型であるものが存在しないからです。
if (0 == TestClass::getMember) { がエラーになるのは、
==演算子のオペランド(std::operator== の引数) が
TestClass::T型であるものが存在しないからです。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 13:10
by かずま
かずま さんが書きました:かずま さんが書きました:
==演算子のオペランド(std::operator== の引数) が
すみません。std::operator== ではなく、::operator== です。
Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい
Posted: 2017年8月21日(月) 22:09
by sereparu
inemaruさん、かずまさん、ご回答ありがとうございます。
inemaruさんのsafe_call()を使う方法も、かずまさんの関数オブジェクトを使う方法も
どちらも実現したいことにかなり近いです。
ただ、すべての関数に対応させるのは難しいので、今作っているプログラムの規模を考えて
基本は括弧の付け忘れに気をつけて、特別注意が必要なものに限定して対応させたいと思います。
一応、具体的な解決策が分かりましたので、解決とさせていただきます。
ご回答いただいた皆様、本当にありがとうございました。