web-dev-qa-db-ja.com

メンバー関数を基本クラスから派生クラスに移動すると、明白な理由もなくプログラムが中断します

この(作成された)質問は、最初はパズルとして定式化され、問題をより迅速に確認するのに役立つ可能性のある詳細の一部を隠しています。下にスクロールして、より単純な [〜#〜] mcve [〜#〜] バージョンを探します。


オリジナル(a-la puzzle)バージョン

_0_を出力する次のコードがあります:

_#include <iostream>
#include <regex>

using namespace std;

regex sig_regex("[0-9]+");
bool oldmode = false;

template<class T>
struct B
{
    T bitset;

    explicit B(T flags) : bitset(flags) {}

    bool foo(T n, string s)
    {
        return bitset < 32                   // The mouth is not full of teeth
               && 63 > (~n & 255) == oldmode // Fooness holds
               && regex_match(s, sig_regex); // Signature matches
    }
};

template<class T>
struct D : B<T>
{
    D(T flags) : B<T>(flags) {}

};

int main()
{
    D<uint64_t> d(128 | 16 | 1);
    cout << d.foo(7, "123") << endl;
}
_

ただし、関数foo()BからDに移動すると、_1_の出力が開始されます( 証明はColiruにあります )。

なぜこれが起こるのですか?


MCVEバージョン

Live on Coliru

_#include <iostream>
#include <bitset>

using namespace std;

template<class T>
struct B
{
    T bitset{0};

    bool foo(int x)
    {
        return bitset < 32 && 63 > (x + 1) == x % 2;
    }
};

template<class T>
struct D : B<T>
{
    bool bar(int x) // This is identical to B<T>::foo()
    {
        return bitset < 32 && 63 > (x + 1) == x % 2;
    }
};

int main()
{
    D<uint64_t> d;
    cout << d.foo(1) << endl; // outputs 1
    cout << d.bar(1) << endl; // outputs 0; So how is bar() different from foo()?
}
_
51
Leon

これがusing namespace std;を決してすべきではない理由です

bool foo(T n, string s)
{
    return bitset < 32                  
           && 63 > (~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

bitsetはあなたが思っているものではありません。 B<T>は依存する基本クラスであるため、メンバーは非修飾ルックアップから隠されます。 bitsetにアクセスするには、thisからアクセスする必要があります1、または明示的に修飾します(詳細については here を参照):

(this->bitset)
B<T>::bitset

派生ケースではbitsetB<T>::bitsetに名前を付けていないため、それはどういう意味ですか?さて、あなたはusing namespace std;を書いたので、実際はstd::bitsetであり、残りの式はたまたま有効です。発生することは次のとおりです。

bool foo(T n, string s)
{
    return std::bitset<32 && 63>(~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

32 && 63は、1uテンプレート引数のstd::bitsetに昇格されるtrueに評価されます。このstd::bitset~n & 255で初期化され、oldmodeと等しいかどうかがチェックされます。 std::bitsetには、oldmodeから一時的なstd::bitset<1>を構築できる非明示的なコンストラクターがあるため、この最後のステップは有効です。


1 この場合、かなり微妙な構文解析のあいまいさの規則のため、this->bitsetを括弧で囲む必要があることに注意してください。詳細については、 テンプレートに依存するベースメンバーが適切に解決されない を参照してください。

90
TartanLlama

はい、bitsetは非依存名として解釈され、std::bitset<T>という名前のテンプレートがあるため、次のように解析されます。

template<class T>
struct D : B<T>
{
    D(T flags) : B<T>(flags) {}
    bool foo(T n, string s)
    {
        return ((std::bitset < 32  && 63 > (~n & 255)) == oldmode)
               && regex_match(s, sig_regex);
    }
};

次のようにする必要があります。

template<class T>
struct D : B<T>
{
    D(T flags) : B<T>(flags) {}

    bool foo(T n, string s)
    {
        // or return B<T>::bitset
        return (this->B<T>::bitset < 32)                   // The mouth is not full of teeth
               && 63 > (~n & 255) == oldmode // Fooness holds
               && regex_match(s, sig_regex); // Signature matches
    }
};

または、using namespace std;を使用しないでください

18
Danh
  1. なぜこれが起こるのですか?

派生クラスの場合、B<T>は非依存基本クラスではありません。テンプレート引数を知らないと決定できません。 bitsetは非依存名であり、依存ベースクラスで検索されません。代わりに、std::bitsetはここで使用されます(using namespace std;)。だからあなたは得るでしょう:

return std::bitset<32 && 63>(~n & 255) == oldmode
       && regex_match(s, sig_regex);

名前をbitsetに依存させることができます。従属名はインスタンス化の時点でのみ検索でき、その時点で調査する必要がある正確なベースの専門化がわかるためです。例えば:

return this->bitset < 32                   // The mouth is not full of teeth
//     ~~~~~~
       && 63 > (~n & 255) == oldmode       // Fooness holds
       && regex_match(s, sig_regex);       // Signature matches

または

return B<T>::bitset < 32                   // The mouth is not full of teeth
//     ~~~~~~
       && 63 > (~n & 255) == oldmode       // Fooness holds
       && regex_match(s, sig_regex);       // Signature matches

または

using B<T>::bitset;
return bitset < 32                   // The mouth is not full of teeth
       && 63 > (~n & 255) == oldmode // Fooness holds
       && regex_match(s, sig_regex); // Signature matches
  1. 回答後、この質問のタイトルは何にすべきですか?

「テンプレート基本クラスの非依存名にアクセスする方法は?」

8
songyuanyao

これは本当にクールな例です!!! :)

私は思う-これは何が起こっているのか:

bitset < 32 && 63 >(~n & 255)

構文解析はbitsetを構成します

3
Nim