Visual C ++不能推导出在ctor中用作默认值的函数的给定(!)模板参数

时间:2013-01-18 14:45:09

标签: c++ templates visual-c++

我正在尝试使用模板化函数来返回模板类构造函数中参数的默认值。该函数的模板参数也是该类的模板参数。我在下面提供了一个例子。

背景 该示例显示了我的应用程序中也出现的确切用例和依赖关系。类X实际上是一个非常大的类,它管理一个大数据块,它被分成更小的块。 Class Helper是一个内存管理器,它以较小块的大小分配和释放内存。实际上,GetHelper函数会尝试在运行时推导出Helper的一些构造函数参数,所以这就是我使用这种设计的原因。

实际问题 当定义宏USE_NS和SHOW_ERROR时,代码不会编译,在第66行给出错误 C2783无法推断出模板参数。这是我尝试使用模板化函数初始化构造函数参数的地方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'

3 个答案:

答案 0 :(得分:2)

突发新闻

在报告Clang 3.2上的问题后,看起来这实际上是一个C ++缺陷。你看到了bug discussion in the clang database

引用理查德史密斯:

  

这是C ++本身的一个缺陷:

     

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

     

Clang匹配所有其他C ++编译器的行为;这是   不是一个错误(虽然取决于该问题的方式   已经解决,我们可能需要重新审视这一点。)

     

解决方法是在默认参数周围添加括号   表达

问题是含糊不清。例如,通过查看:

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

...是一个参数,而且......

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

我们可以意识到,区分模板参数,常规<调用以及因此构成默认参数是不容易的。

如果我们遵循理查德的建议并添加括号,那么我们就不再受编译器的限制。或者至少I can guarantee Clang gets it right(修订版6):

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

它看起来像编译器错误。

我测试了你的代码(感谢你顺便发布一个完整的例子)on 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.

我用两种方式稍微修改了这个例子(改变5):

  • static boost::shared_ptr<Helper> Get() { return boost::shared_ptr<Helper>(new Helper()); }模板中引入了Helper内联

  • typedef NH::Helper< TNumAtX, TSize > Helper中制作了X并将其用于构造函数X(boost::shared_ptr<Helper> h = Helper::Get());

现在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>() ) {}
};

你可以see it here

答案 1 :(得分: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

答案 2 :(得分:0)

编译器抛出此错误的原因是因为模板参数之间存在歧义。辅助类需要模板参数,x也是如此。编译器不知道如何构建Helper,因为提供的参数是另一组模板参数。

x的构造函数:

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

正在尝试为模板成员分配默认值。它不知道向Helper和GetHelper提供了哪些模板参数。即使模板相同,您也必须明确声明x中的模板参数作为Helper和GetHelper的模板参数传递。

这篇文章中有关于模板模板参数的一些有见地的答案: Template Template Parameters

另外,基于msdn网站,有关于C2783的声明: MSDN

  

编译器无法确定模板参数。默认参数不能用于推导模板参数。