我正在尝试使用模板化函数来返回模板类构造函数中参数的默认值。该函数的模板参数也是该类的模板参数。我在下面提供了一个例子。
背景 该示例显示了我的应用程序中也出现的确切用例和依赖关系。类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'
答案 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
编译器无法确定模板参数。默认参数不能用于推导模板参数。