【问题标题】:Variadic templated constructor does not take x arguments可变模板构造函数不接受 x 参数
【发布时间】:2017-03-23 00:15:10
【问题描述】:

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

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&lt; DerivedResourceT &gt;(*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

【问题讨论】:

    标签: c++ templates variadic-templates


    【解决方案1】:

    需要注意的一点(这可能是也可能不是问题的最终原因)是您在模板参数转发方面没有做完全正确的事情。例如,在您传递 ID3D11Device2 的情况下,您的 DerivedResourceT 构造函数可能(根据您的非可变构造函数的签名判断)需要一个引用 - 但是由于模板推导的工作方式,它实际上会得到一个而是复制(如果这确实是允许的 - 如果不是,它将无法编译)。

    要纠正这个问题,您需要使用标准转发配方,它允许传递传递参数的正确左值或右值,其中包括正确转发引用:

    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...的参数类型中的&amp;&amp;std::forward调用。

    【讨论】:

    • 这似乎是解决方案,但看起来像中文。以前从未使用过引用到引用 &&。而对于中间调用 GetResource -> GetDerivedResource && 不应该使用?
    • 是的,你应该 - 在转发可能是 l 值或 r 值的模板参数并且你不知道哪个预先设置时,应该始终使用完美的转发配方。 Here 是一个解释右值引用的链接(&amp;&amp; 语法),here 是一个解释完美转发的链接
    • 感谢您的参考。这是有道理的(尽管原始类型是沿整个链通过引用传递的)。
    猜你喜欢
    • 2018-05-18
    • 2016-09-02
    • 2015-05-06
    • 2016-01-02
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-29
    相关资源
    最近更新 更多