LoginSignup
2
2

More than 5 years have passed since last update.

VisualStudio(2015)ではconstメンバー関数と非constメンバー関数でテンプレートの型推論をしてくれない

Posted at

開発するのにVisualStudioの強力なIDE機能を使いたいのでVisualStudio大好き人間なのですが、なにせコア機能が弱いところがあるのが難点だと言われています。
よく言われるのはC++11のサポートですが、C++11でなくてもVisualStudioが弱いところをまた見つけてしまいました・・・。

タイトルちょっと分かりにくいのですが、以下の様なことです。

NonConst.cpp
#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;
}

ここで、

  1. 関数Fooは、constメンバー関数(以降「前者」)と非constメンバー関数(以降「後者」)があります。
  2. 関数Funcは、非constメンバー関数を受け取る関数です。
  3. ということは、Func(arg)と呼んだときは、明らかにFuncの(実)引数argは非constメンバー関数でなければなりません。
  4. ならば、Func(&S::Foo)&S::Fooは、「後者」の非constメンバー関数を指していることが分かります。
  5. 後者ならTdoubleと型推測できます。

ということで、clanggccではこれは普通にコンパイル&実行できます。
ただ、なぜかVisualStudio(2015で試しました)では「テンレンプレート引数Tに対して減少できませんでした」となってコンパイルできないんですよね・・・。
型推論エンジンが弱いのかなんなのかはちょっと分かりません。

なお、Funcをconstメンバー関数を呼ぶようにすると

Const.cpp
#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;
}

clanggccはもちろんちゃんと動きますが、VisualStudio(2015)だとコンパイル時の「Tに対して減少できませんでした」に加えて、IntelliSenseが「S::Fooのどのインスタンスが対象であるかを特定できません」とエラーを吐いてます。
お前はconstメンバー関数ポインタに非constメンバー関数ポインタを代入できると思っているのか・・・?(※ちなみに単純に試したらさすがにそれは型エラーになってくれました)

まぁ、実は言語仕様上は未定義動作でclangとgccはたまたま動いているだけ、という可能性は否定できませんが・・・。

なお、テンプレートの型推論を必要としない場合

NonTemplate.cpp
#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の呼び出し時に型を明示的に指定すれば解決するのですが・・・困ったものです。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2