VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
sereparu
記事: 17
登録日時: 2年前

VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#1

投稿記事 by sereparu » 1年前

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;
}

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#2

投稿記事 by みけCAT » 1年前

sereparu さんが書きました:例えば、あるクラス(TestClass)の静的メンバ変数(member)の値を返すgetter関数を呼び出す際に
「TestClass::getMember()」のように括弧ありで呼び出すと、正常にメンバ変数の値が返りますが
「TestClass::getMember」のように括弧なしで呼び出すと、異常な値(「001513BB」などの16進数(?)のような値)が返ります。
後者は関数を呼び出しているのではなく、関数のアドレスを得ているだけです。
sereparu さんが書きました:また、括弧なしの関数の呼び出しは、どのような場合に使用するのでしょうか?
括弧なしの関数の呼び出しは、括弧を使わなくても関数を呼び出せる言語を使う場合に使用します。
また、C++においても、演算子のオーバーロードを用いると、括弧を用いずに関数を呼び出せます。
関数を呼び出さずにアドレスを取得するのは、関数ポインタ変数に関数のアドレスを代入して処理を切り替える場合などに使用します。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sereparu
記事: 17
登録日時: 2年前

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#3

投稿記事 by sereparu » 1年前

みけCATさん、迅速なご回答誠にありがとうございます。
みけCAT さんが書きました: 後者は関数を呼び出しているのではなく、関数のアドレスを得ているだけです。
括弧なしの呼び出しだと全く別の処理になってしまうんですね。
知らなかったので確認できて良かったです。
みけCAT さんが書きました: また、C++においても、演算子のオーバーロードを用いると、括弧を用いずに関数を呼び出せます。
「括弧を用いずに関数を呼び出せる」というのは
「TestClass::getMember」と書いても「TestClass::getMember()」と
同じ結果が得られるようになる、という意味でしょうか?
演算子のオーバーロードは使ったことがないので、方法が分かりません。

演算子のオーバーロードについて調べて、自分なりに考えてみました。
例えば、上記コードの例だと、TestClassに

コード:

	int operator +() {
		return getMember();
	}
というように演算子のオーバーロードを追加して
TestClassのオブジェクト(obj)を作って、「+obj」とすればメンバ変数の値を得ることができました。

この方法なら、確かに括弧を用いずに関数を呼び出せる、ということになりますが
当初の目的とは異なってしまいます。

あくまで目的は、関数呼び出し時に末尾の括弧を付け忘れても意図しない動作にならないよう
括弧の付け忘れに気付けるようにするか、括弧を付けた場合と同じ動作にできるようにすることです。

もし、演算子のオーバーロードを用いれば、「TestClass::getMember」と書いても
「TestClass::getMember()」と同じ結果が得られるようになる、という意味でご回答いただいたのでしたら
よろしければ、上記コードの例を用いた具体的な方法をご回答いただけますでしょうか?

よろしくお願い致します。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#4

投稿記事 by みけCAT » 1年前

sereparu さんが書きました:「括弧を用いずに関数を呼び出せる」というのは
「TestClass::getMember」と書いても「TestClass::getMember()」と
同じ結果が得られるようになる、という意味でしょうか?
いいえ、そのような意図ではありません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Math

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#5

投稿記事 by Math » 1年前

コード:

#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は関数ポインターでありエラーにはできませんね。

sereparu
記事: 17
登録日時: 2年前

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#6

投稿記事 by sereparu » 1年前

Mathさん、ご回答ありがとうございます。

関数ポインターは使ったことがないですが、そういう使い方があるんですね。
エラーにならない以上、やっぱり括弧を付けるように気をつけるしかないでしょうか。


inemaru
記事: 108
登録日時: 3年前

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#8

投稿記事 by inemaru » 1年前

「関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい」 ではなくて、
「想定外の呼び出しをエラーにする」に妥協するならば、
エラーを出すことは可能ですが、どうでしょう?

コード:

#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++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#9

投稿記事 by かずま » 1年前

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++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#10

投稿記事 by かずま » 1年前

かずま さんが書きました: 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++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#11

投稿記事 by かずま » 1年前

かずま さんが書きました:
かずま さんが書きました: ==演算子のオペランド(std::operator== の引数) が
すみません。std::operator== ではなく、::operator== です。

sereparu
記事: 17
登録日時: 2年前

Re: VC++で関数呼び出し時に末尾の括弧がない場合にコンパイルエラーにしたい

#12

投稿記事 by sereparu » 1年前

inemaruさん、かずまさん、ご回答ありがとうございます。

inemaruさんのsafe_call()を使う方法も、かずまさんの関数オブジェクトを使う方法も
どちらも実現したいことにかなり近いです。

ただ、すべての関数に対応させるのは難しいので、今作っているプログラムの規模を考えて
基本は括弧の付け忘れに気をつけて、特別注意が必要なものに限定して対応させたいと思います。

一応、具体的な解決策が分かりましたので、解決とさせていただきます。
ご回答いただいた皆様、本当にありがとうございました。

返信

“C言語何でも質問掲示板” へ戻る