開発するのにVisualStudioの強力なIDE機能を使いたいのでVisualStudio大好き人間なのですが、なにせコア機能が弱いところがあるのが難点だと言われています。
よく言われるのはC++11のサポートですが、C++11でなくてもVisualStudioが弱いところをまた見つけてしまいました・・・。
タイトルちょっと分かりにくいのですが、以下の様なことです。
#include <iostream>
struct S
{
S() {}
int Foo()
{
std::cout << "Non-Const : ";
return 1;
}
double Foo() const
{
std::cout << "Const : ";
return 0.1;
}
template<typename T>
void Func(T(S::*f)())
{
std::cout << (this->*f)() << std::endl;
}
};
int main()
{
S().Func(&S::Foo);
return 0;
}
ここで、
- 関数
Foo
は、constメンバー関数(以降「前者」)と非constメンバー関数(以降「後者」)があります。 - 関数
Func
は、非constメンバー関数を受け取る関数です。 - ということは、
Func(arg)
と呼んだときは、明らかにFunc
の(実)引数arg
は非constメンバー関数でなければなりません。 - ならば、
Func(&S::Foo)
の&S::Foo
は、「後者」の非constメンバー関数を指していることが分かります。 - 後者なら
T
はdouble
と型推測できます。
ということで、clangやgccではこれは普通にコンパイル&実行できます。
ただ、なぜかVisualStudio(2015で試しました)では「テンレンプレート引数Tに対して減少できませんでした」となってコンパイルできないんですよね・・・。
型推論エンジンが弱いのかなんなのかはちょっと分かりません。
なお、Func
をconstメンバー関数を呼ぶようにすると
#include <iostream>
struct S
{
S() {}
int Foo()
{
std::cout << "Non-Const : ";
return 1;
}
double Foo() const
{
std::cout << "Const : ";
return 0.1;
}
template<typename T>
void F(T(S::*f)() const) const
{
std::cout << (this->*f)() << std::endl;
}
};
int main()
{
S().F(&S::Foo);
return 0;
}
clangとgccはもちろんちゃんと動きますが、VisualStudio(2015)だとコンパイル時の「Tに対して減少できませんでした」に加えて、IntelliSenseが「S::Fooのどのインスタンスが対象であるかを特定できません」とエラーを吐いてます。
お前はconstメンバー関数ポインタに非constメンバー関数ポインタを代入できると思っているのか・・・?(※ちなみに単純に試したらさすがにそれは型エラーになってくれました)
まぁ、実は言語仕様上は未定義動作でclangとgccはたまたま動いているだけ、という可能性は否定できませんが・・・。
なお、テンプレートの型推論を必要としない場合
#include <iostream>
struct S
{
S() {}
int Foo()
{
std::cout << "Non-Const : ";
return 1;
}
int Foo() const
{
std::cout << "Const : ";
return -1;
}
void F(int(S::*f)())
{
std::cout << (this->*f)() << std::endl;
}
};
int main()
{
S().F(&S::Foo);
return 0;
}
はちゃんと動いてくれるんですよね。
ということで、とりあえず型推論してくれないだけなので、Fの呼び出し時に型を明示的に指定すれば解決するのですが・・・困ったものです。