可变参数模板构造函数不带x参数

问题描述:

对于模板类的内部模板结构,我想要一个可变模板构造函数。不幸的是,构造函数(参见下面的第一个构造函数)是不够的:如果仅使用该构造函数,则会获得C2260编译器错误,指出构造函数不带3,4或5个参数。另一方面,通过添加另外三个构造函数(请参见下面的其余构造函数)使所有内容都可以按预期工作。可变参数模板构造函数不带x参数

template< typename KeyT, typename ResourceT > 
class ResourcePool { 

    ... 

    template< typename DerivedResourceT > 
    struct ResourcePoolEntry final : public DerivedResourceT { 

     template< typename... ConstructorArgsT > 
     ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ConstructorArgsT... args) 
      : DerivedResourceT(args...), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 

     ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ID3D11Device2 &x) 
      : DerivedResourceT(x), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 

     ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ID3D11Device2 &x, const wstring &y) 
      : DerivedResourceT(x,y), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 

     template < typename VertexT > 
     ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ID3D11Device2 &x, const wstring &y, const MeshDescriptor<VertexT> &z) 
      : DerivedResourceT(x, y, z), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 

      ... 
    } 
} 

调用构造函数是这样的:

template< typename KeyT, typename ResourceT > 
template< typename... ConstructorArgsT > 
std::shared_ptr<ResourceT> ResourcePool< KeyT, ResourceT >::GetResource(KeyT key, ConstructorArgsT... args) { 
    return GetDerivedResource< ResourceT, ConstructorArgsT... >(key, args...); 
} 

template< typename KeyT, typename ResourceT > 
template< typename DerivedResourceT, typename... ConstructorArgsT > 
std::shared_ptr<ResourceT> ResourcePool< KeyT, ResourceT >::GetDerivedResource(KeyT key, ConstructorArgsT... args) { 
    ... 
    auto new_resource = std::shared_ptr< ResourcePoolEntry<DerivedResourceT> >(
     new ResourcePoolEntry<DerivedResourceT>(*this, key, args...)); 
    ... 
} 

对于原始像bool作为可变参数的说法,一切工作正常。

Severity Code Description Project File Line Suppression State 
Error C2660  'mage::ResourcePool<std::wstring,mage::VertexShader>::ResourcePoolEntry<DerivedResourceT>::ResourcePoolEntry': function does not take 3 arguments MAGE c:\users\matthias\documents\visual studio 2015\projects\mage\mage\mage\src\resource\resource_pool.tpp 37 

哪里线37对应于构造函数调用(new ResourcePoolEntry<DerivedResourceT>(*this, key, args...));在上面的例子中)

我在做什么错? (编译器MSVC++ 14.0)

最小示例:

#include <memory> 
#include <map> 

template < typename T > 
using SharedPtr = std::shared_ptr<T>; 

template < typename T > 
using WeakPtr = std::weak_ptr<T>; 

template< typename KeyT, typename ResourceT > 
using ResourceMap = std::map< KeyT, WeakPtr<ResourceT> >; 

template< typename KeyT, typename ResourceT > 
class ResourcePool { 

public: 

    template< typename... ConstructorArgsT > 
    SharedPtr<ResourceT> GetResource(KeyT key, ConstructorArgsT... args); 
    template< typename DerivedResourceT, typename... ConstructorArgsT > 
    SharedPtr<ResourceT> GetDerivedResource(KeyT key, ConstructorArgsT... args); 

private: 

    ResourceMap< KeyT, ResourceT > m_resource_map; 

    template< typename DerivedResourceT > 
    struct ResourcePoolEntry final : public DerivedResourceT { 

    public: 

     template< typename... ConstructorArgsT > 
     ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ConstructorArgsT... args) 
      : DerivedResourceT(args...), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 
    private: 

     ResourcePool< KeyT, ResourceT > &m_resource_pool; 
     KeyT m_resource_key; 
    }; 
}; 

template< typename KeyT, typename ResourceT > 
template< typename... ConstructorArgsT > 
SharedPtr<ResourceT> ResourcePool< KeyT, ResourceT >::GetResource(KeyT key, ConstructorArgsT... args) { 
    return GetDerivedResource< ResourceT, ConstructorArgsT... >(key, args...); 
} 

template< typename KeyT, typename ResourceT > 
template< typename DerivedResourceT, typename... ConstructorArgsT > 
SharedPtr<ResourceT> ResourcePool< KeyT, ResourceT >::GetDerivedResource(KeyT key, ConstructorArgsT... args) { 
    auto it = m_resource_map.find(key); 
    if (it != m_resource_map.end()) { 
     auto resource = it->second.lock(); 
     if (resource) { 
      return resource; 
     } 
     else { 
      m_resource_map.erase(it); 
     } 
    } 

    auto new_resource = SharedPtr< ResourcePoolEntry<DerivedResourceT> >(
     new ResourcePoolEntry<DerivedResourceT>(*this, key, args...)); 
    m_resource_map[key] = new_resource; 
    return new_resource; 
} 

#include <d3d11_2.h> 

struct A { 
}; 
struct B : public A { 
    B(ID3D11Device &device) : A() {} 
}; 


const D3D_FEATURE_LEVEL g_feature_levels[] = { 
    D3D_FEATURE_LEVEL_11_1, 
    D3D_FEATURE_LEVEL_11_0 
}; 

int main() { 

    ID3D11Device *device; 
    ID3D11DeviceContext *device_context; 
    D3D_FEATURE_LEVEL feature_level; 
    D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, 
     g_feature_levels, _countof(g_feature_levels), D3D11_SDK_VERSION, 
     &device, &feature_level, &device_context 
    ); 

    ResourcePool< char, A > *pool = new ResourcePool< char, A >(); 
    //pool->template GetResource< int & >('a'); 
    pool->template GetDerivedResource< B, ID3D11Device & >('b', *device); 
} 

错误

Severity Code Description Line Suppression State 
Error C2661 'ResourcePool<char,A>::ResourcePoolEntry<DerivedResourceT>::ResourcePoolEntry': no overloaded function takes 3 arguments 66 
+1

提供实际的错误文本。在实际线路上。理想情况下,提供一个生成错误的[MCVE]。这可能需要修改您的代码以删除无关的内容。要弄清楚你是否在“删除细节”中排除了重要的东西是非常困难的,阅读你的思想变得很痛苦。我会注意到,你的variardic ctor通过value *来获取*,而你的手动参考则是通过引用和const引用的混合。 – Yakk

+0

@Yakk我的variadic ctor没有采取我通过它的方式吗? – Matthias

+1

它以价值取胜。这可能会导致问题。我不能说,因为你没有提供生成的实际构建错误,我不知道你正在使用的每种类型。再次[MCVE]和实际错误,或者你的问题是不好的。 – Yakk

需要注意的一点(其可以是或可以不是你的问题的根本原因)是你的模板参数转发工作并不是很正确。例如,在你传递一个ID3D11Device2DerivedResourceT构造函数的情况下(可能由你的非变量构造函数的签名来判断)需要一个引用 - 但是由于模板演绎的方式,它实际上会获得一个副本(if事实上,这甚至是可以允许的 - 如果没有的话,它不会编译)。

要纠正这一点,你需要使用标准的转发配方,它允许通过参数的正确1-或R-valueness被传递,其中包括正确转发引用:

template< typename... ConstructorArgsT > 
ResourcePoolEntry(ResourcePool< KeyT, ResourceT > &resource_pool, 
      KeyT resource_key, ConstructorArgsT&&... args) 
      : DerivedResourceT(std::forward<ConstructorArgsT>(args)...), m_resource_pool(resource_pool), m_resource_key(resource_key) {} 

在以上,请注意参数类型args...std::forward调用中的&&

+0

这似乎是解决方案,但它看起来像中文。以前从未使用过reference-to-reference &&。而对于中间调用GetResource - > GetDerivedResource &&不应该使用? – Matthias

+1

是的,你应该 - 在转发可能是l值或r值的模板参数时,总是应该使用完美的转发配方,而且你不知道哪些是前期的。 [Here](http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html)是一个链接,它可以在某种程度上解释r值引用( ''&&语法)和[这里](http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/)是一个完美的解释转发 – Smeeheey

+0

感谢您的参考。它是有道理的(虽然原始类型是通过引用沿着整个链传递的)。 – Matthias