2

これは、非直感的な型推定によって引き起こされた、見つけるのが非常に困難な微妙なバグの例です。プログラマーは、NewValue() 関数のテンプレート引数の型推論から、"var2" が型 "built2" であることを期待していました。実際に起こることは、var2 の推定型が基本クラス「built1」であるということです。派生クラスに ConvertValue() 仮想関数のオーバーライドがある場合、これはコンパイルされ、期待どおりに動作します。

型の不一致を示すためだけに、コンパイラ ブレーカーの行を挿入しました。問題は、NewValue<> が多くの場所で使用され、予期しない型推論が正常に通過して予期しない結果が生じることです。また、プログラマーは、NewValue<> を期待どおりに動作させるためだけに、すべての派生クラスでオーバーロードする必要なく、基本クラスのメンバー関数「ConvertValue()」を継承できることを期待していました。

これはコンパイラによって正しく解釈されていますか、それともコンパイラのバグですか?

class base 
{
    int x;

public:

    base() : x(10){}
    int value() const { return x; }
    void value( int val ) { x = val; }
};

class derived1 : public base
{
public:
    virtual void ConvertValue( int x) { value( Factor()*x); }
    virtual int Factor() const { return 2; }

};

class derived2 : public derived1
{
public:
    //virtual void ConvertValue( int x) { value( Factor()*x); }
    virtual int Factor() const { return 3; }

};

template<typename T>
T NewValue( void (T::*unitsFunc)(int), int value)
{
    T obj;
    (obj.*unitsFunc)(value);
    return obj;
}

int _tmain(int argc, _TCHAR* argv[])
{
    auto var1 = NewValue( &derived1::ConvertValue, 10 );
    auto var2 = NewValue( &derived2::ConvertValue, 10 );

    std::cout   << "Test type deduction from virtual member function" << std::endl << std::endl 
                << "This should have value of 20:   " << var1.value()  << std::endl 
                << "This should have value of 30:   " << var2.value()  << std::endl 
                << (var2.value() == 30 ? "CORRECT" : "INCORRECT - bad type deduction") << std::endl << std::endl ;

    // this will not compile because NewValue<>() is returning type "derived1" instead of "derived2" 
    derived2 test = NewValue( &derived2::ConvertValue, 10 );
    return 0;
}
4

1 に答える 1

1

いいえ、これはバグではありません。結果に適用&するときは常に、宣言され&derived2::ConvertValueているクラスのメンバーへのポインターです。ConvertValueこれは&derived2::ConvertValueisの結果からの型を意味しvoid (derived1::*)(int)ます。derived2(これは、でオーバーライドするとコードがコンパイルされる理由でもあります)

ただし、基本クラスのメンバー関数へのポインターは、派生クラスのメンバーへのポインターに変換できます。これは、 のすべてのインスタンスが継承するderived2メンバー関数を持っているためです。derived1::ConvertValueしたがって、コンパイラにそのように変換するように明示的に指示できます。

auto var2 = NewValue<derived2>( &derived2::ConvertValue, 10 );
于 2013-07-08T22:29:53.513 に答える