4

テンプレート化された関数を使用して、テンプレート クラスのコンストラクターのパラメーターの既定値を返そうとしています。関数のテンプレート パラメーターは、クラスのテンプレート パラメーターでもあります。以下に例を示しました。

背景 この例は、私のアプリケーションでも発生する正確なユースケースと依存関係を示しています。クラス X は実際には、小さなブロックに分割された大きなデータ ブロックを管理する非常に大きなクラスです。クラス ヘルパーは、小さなブロックのサイズでメモリを割り当てて解放するメモリ マネージャです。実際には、GetHelper 関数は実行時にヘルパーのいくつかのコンストラクター パラメーターを推測しようとするため、この設計を使用しました。

実際の質問 マクロ USE_NS と SHOW_ERROR の両方が定義されている場合、コードはコンパイルされず、エラー C2783 could not deduce template argument at line 66 が発生します。ここで、テンプレート化された関数 GetHelper (テンプレートを提供するパラメーター!)。GetHelper は、クラス X とは異なる名前空間からのものであることを思い出してください。72 行目では、ctor 本体内の Helper オブジェクトを初期化するために、同じ関数呼び出しが使用されていることにも注意してください。なにが問題ですか?解決策または回避策はありますか?

私は Visual Studio 2008 Pro を使用しており、1.47 をブーストしています。

#include <iostream>
#include <boost/shared_ptr.hpp>

// 1 means macro is defined
// USE_NS 1 and SHOW_ERROR 1 -> Compileerror
// USE_NS 0 and SHOW_ERROR 1 -> Works, Output: 8 and 16.5
// USE_NS 1 and SHOW_ERROR 0 -> Works, Output: 3
// USE_NS 0 and SHOW_ERROR 0 -> Works, Output: 3


#define USE_NS
#define SHOW_ERROR


#ifndef USE_NS
#define NH 
#define NX
#endif

#ifdef USE_NS
namespace NH
{
#endif

template< typename TNumAtH, size_t TSizeAtH>
class Helper
{
public:
    TNumAtH* x;

    Helper()
    {
        x = new TNumAtH[TSizeAtH];
        x[0] = static_cast< TNumAtH >( 8 );
    }

    ~Helper()
    {
        delete [] x;
    }
};

template <typename TNumAtF, size_t TSizeAtF >
boost::shared_ptr< Helper< TNumAtF, TSizeAtF > > GetHelper()
{
    return boost::shared_ptr< Helper< TNumAtF,TSizeAtF > > ( new Helper< TNumAtF, TSizeAtF >() );
}

#ifdef USE_NS
}

namespace NX
{
#endif

template< typename TNumAtX, size_t TSize >
class X
{
public:
    boost::shared_ptr< NH::Helper< TNumAtX, TSize > > a;

#ifdef SHOW_ERROR
    //this produces an error if namespace are used, if no namespaces are used
    //NH is reduced to a blank (see macros at line 17 and 18
    X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );
#endif

    X( TNumAtX firstElem )
    {
        //this works with namespaces and without namespaces
        a = NH::GetHelper< TNumAtX, TSize >();
        a->x[0] = firstElem;
    }

    TNumAtX GetNum()
    {
        return a->x[0];
    }

};

#ifdef SHOW_ERROR
template< typename TNumAtX, size_t TSize >
X<TNumAtX, TSize>::
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h ) : a( h )
{
}
#endif

#ifdef USE_NS
}
#endif


int main( int argc, char** argv )
{
    std::cout << "Hello at TestTemplateFunction" << std::endl;
    //use case one
#ifdef SHOW_ERROR
    NX::X<int, 5> x1;
#else
    NX::X<int, 5> x1( 3 );
#endif
    std::cout << "Use case 1: " << x1.GetNum() << std::endl;

    //use case two
#ifdef SHOW_ERROR
    typedef float T;
    size_t const N = 9;
    boost::shared_ptr< NH::Helper<T, N> > h( new NH::Helper<T, N> );
    h->x[0] = 16.5f;
    NX::X<T, N> x2( h );
    std::cout << "Use case 2: " << x2.GetNum() << std::endl;
#endif

    std::cout << "Hit the any key" << std::endl;
    getchar();
    return 0;  
}

ここに CMakeLists.txt ファイルがあります

PROJECT(TestTemplateFunction)
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )

FIND_PACKAGE( BOOST )

INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} )

ADD_EXECUTABLE( TestTemplateFunction main.cpp  )

編集:コンパイルエラー

main.cpp(65) : error C2783: 'boost::shared_ptr<NH::Helper<TNumAtH,TSizeAtH>>    NH::GetHelper(void)' : could not deduce template argument for 'TNumAtF'
main.cpp(44) : see declaration of 'NH::GetHelper'
main.cpp(65) : error C2783: 'boost::shared_ptr<NH::Helper<TNumAtH,TSizeAtH>>       NH::GetHelper(void)' : could not deduce template argument for 'TSizeAtF'
main.cpp(44) : see declaration of 'NH::GetHelper'
4

3 に答える 3

2

ニュース速報

Clang 3.2 の問題を報告した後、これは実際には C++ の欠陥であると思われます。clang データベースにバグの議論が表示されます。

リチャード・スミスの引用:

これは C++ 自体の欠陥です。

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#325

ここでは、Clang は他のすべての C++ コンパイラの動作と一致します。これはバグではありません (ただし、問題の解決方法によっては、再検討する必要がある場合があります)。

これを回避するには、デフォルトの引数式を括弧で囲みます。

問題はあいまいさの 1 つです。たとえば、以下を参照してください。

int = a < b, c < d > ( e )

... は 1 つのパラメーターであり、...

int = a < b, c < d > ( e ) = 0

<テンプレート パラメーター、通常の呼び出し、およびデフォルトの引数を構成するものを区別するのは簡単ではないことがわかります。

リチャードの提案に従って括弧を追加すると、コンパイラの気まぐれに左右されなくなります。または、少なくとも、Clang が正しく動作することを保証できます(リビジョン 6):

 X( boost::shared_ptr< NH::Helper<T, N> > h = (NH::GetHelper<T, N>()) );

コンパイラのバグのように見えます。

liveworkspaceでコードをテストしました (ちなみに完全な例を投稿していただきありがとうございます) 。gcc 4.7.2 は次の出力を生成します。

Hello at TestTemplateFunction
Use case 1: 8
Use case 2: 16.5

一方、clang 3.2 は以下を生成します。

Compilation finished with errors:
source.cpp:65:86: error: unknown type name 'TSize'
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );
                                                                                 ^
source.cpp:65:92: error: expected ')'
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );
                                                                                       ^
source.cpp:65:6: note: to match this '('
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );
 ^
source.cpp:65:84: error: expected '>'
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );
                                                                               ^
source.cpp:85:1: error: out-of-line definition of 'X<TNumAtX, TSize>' does not match any declaration in 'X<TNumAtX, TSize>'
X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h ) : a( h )
^
4 errors generated.

この例を 2 つの方法で少し変更しました (変更 5)。

  • テンプレートstatic boost::shared_ptr<Helper> Get() { return boost::shared_ptr<Helper>(new Helper()); }にインラインで導入Helper

  • typedef NH::Helper< TNumAtX, TSize > Helperinを作成し、Xそれをコンストラクターに使用しましたX(boost::shared_ptr<Helper> h = Helper::Get());

そして今、clang はなんとかコードをコンパイルします。

(clangの)エラーを次のように減らすことができました。

template <typename T, unsigned N>
struct Helper {};

template <typename T, unsigned N >
Helper< T, N > GetHelper() { return Helper< T, N > (); }

template < typename T, unsigned N >
struct X {
    X( Helper< T, N > h = GetHelper<T, N>() ) {}
};

ここで見ることができます。

于 2013-01-18T16:29:23.913 に答える
1

これは非常に奇妙な状況であり、名前空間が与えられたときになぜこのように動作するのか、まだ理解できません。

しかし、私はあなたが使用できる回避策を見つけました。
名前空間を使用していることを宣言し、関数の前に名前空間スコープを削除するだけです。

namespace NX
{
using namespace NH;
#endif

template< typename TNumAtX, size_t TSize >
class X
{
public:
    boost::shared_ptr< NH::Helper< TNumAtX, TSize > > a;

#ifdef SHOW_ERROR
    //this produces an error if namespace are used, if no namespaces are used
    //NH is reduced to a blank (see macros at line 17 and 18
            X( std::shared_ptr< NH::Helper< TNumAtX, TSize > > h = GetHelper<TNumAtX, TSize> () );
#endif
于 2013-01-19T11:12:07.423 に答える
0

コンパイラがこれをスローする理由は、テンプレート引数の間にあいまいさがあるためです。ヘルパー クラスにはテンプレート引数が必要であり、x も同様です。提供されたパラメーターはテンプレート引数の別のセットであるため、コンパイラーはヘルパーの作成方法を知りません。

x のコンストラクタ:

X( boost::shared_ptr< NH::Helper< TNumAtX, TSize > > h = NH::GetHelper< TNumAtX, TSize >() );

テンプレートメンバーにデフォルト値を割り当てようとしています。Helper および GetHelper に提供されているテンプレート引数はわかりません。テンプレートは同じですが、x からのテンプレート パラメーターが Helper と GetHelper のテンプレート引数として渡されることを明示的に述べる必要があります。

この投稿には、テンプレート テンプレート パラメーターに関する洞察に満ちた回答がいくつかあります: テンプレート テンプレート パラメーター

また、msdn Web サイトに基づいて、C2783 に関する次のステートメントがあります: MSDN

コンパイラはテンプレート引数を決定できません。デフォルトの引数を使用して、テンプレートの引数を推測することはできません。

于 2013-01-18T16:07:50.573 に答える