4

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

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

实际问题 当两个宏 USE_NS 和 SHOW_ERROR 都定义时,代码不会编译,在第 66 行给出错误 C2783 could not deduc template argument。这是我尝试使用模板函数 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++ 编译器的行为;这不是一个错误(尽管取决于解决该问题的方式,我们可能需要重新审视这一点)。

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

问题之一是模棱两可。例如,通过查看:

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

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

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

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

如果我们遵循 Richard 的建议并添加括号,那么我们将不再受制于编译器的奇思妙想。或者至少,我可以保证 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.

我以两种方式稍微修改了示例(更改 5):

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

  • 制作了一个typedef NH::Helper< TNumAtX, TSize > Helper并将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 也是如此。编译器不知道如何构建 Helper,因为提供的参数是另一组模板参数。

您的 x 构造函数:

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

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

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

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

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

于 2013-01-18T16:07:50.573 回答