【问题标题】:Specializing "type argument" with "non-type argument" (or the other way around)用“非类型参数”(或相反)专门化“类型参数”
【发布时间】:2015-01-02 12:31:25
【问题描述】:

下面的玩具示例演示了我想要实现的目标。

#include <cstddef>

template<size_t ElementSize>
class Buffer
{
public:

    char buffer[ElementSize];
};

template<typename T>
class Buffer<sizeof(T)>
{
public:

    char buffer[sizeof(T)];
};

int main()
{
    Buffer<4> b1;   // buffer with 4 bytes
    Buffer<int> b2; // buffer with space for "int"
}

这段代码显然不能编译:

$ g++ test.cpp 
test.cpp:12:7: error: template argument ‘sizeof (T)’ involves template parameter(s)
 class Buffer<sizeof(T)>
       ^
test.cpp: In function ‘int main()’:
test.cpp:22:12: error: type/value mismatch at argument 1 in template parameter list for ‘template<long unsigned int ElementSize> class Buffer’
  Buffer<int> b2; // buffer with space for "int"
            ^
test.cpp:22:12: error:   expected a constant of type ‘long unsigned int’, got ‘int’
test.cpp:22:16: error: invalid type in declaration before ‘;’ token
  Buffer<int> b2; // buffer with space for "int"

有什么方法可以让我有两种特殊化的模板 - 一种使用以字节为单位的显式大小(非类型参数),另一种使用类型 T(类型参数)的大小(使用 sizeof()) ?我对不需要两个具有不同名称的单独模板或任何 #define 宏的解决方案感兴趣。

我尝试在“反向”中实现它 - 将 &lt;typename T&gt; 作为主要模板,使用 size_t 作为专业化模板(将 char[sizeof(T)]std::aligned_storage&lt;...&gt;::type 传递给主要模板),但是失败了也是。

【问题讨论】:

    标签: c++ templates partial-specialization


    【解决方案1】:

    不,你不能。一旦我们拥有template &lt;size_t Size&gt; class Buffer,就专业化而言,我们可以做的事情非常有限。即,§14.5.5/8.1:

    部分特化的非类型实参表达式不应包含 部分特化,除非参数表达式是一个简单的标识符[例子:

    template <int I, int J> struct A {};
    template <int I> struct A<I+5, I*2> {}; // error
    
    template <int I, int J> struct B {};
    template <int I> struct B<I, I> {}; // OK
    

    - 结束示例]

    所以我们唯一能做的部分专业化是:

    template <size_t I> class Buffer<I> { .. };
    

    违反了 §14.5.5/8.3

    特化的参数列表不应与主模板的隐式参数列表相同。

    在另一个方向,我们与 14.5.5/8.4 发生冲突:

    专业化应该比主模板更专业化

    size_t 绝不比 T 更专业。

    你可以做的只是创建一个别名:

    template <size_t ElementSize>
    class Buffer {
        char buffer[ElementSize];
    };
    
    template <typename T>
    using BufferFromType = Buffer<sizeof(T)>;    
    
    // or, if not C++11, another type
    template <typename T>
    class BufferFromType : public Buffer<sizeof(T)> { };
    

    【讨论】:

    • 我对这个答案并不感到兴奋。必须有更好的方法来从标准中解释这一点,我就是找不到。
    【解决方案2】:

    类模板不能重载。如果类模板Buffer 采用size_t 类型的非类型参数,那么每次编写Buffer&lt;thing&gt; 时,thing 必须是有效的非类型参数;它不能是一种类型。反之亦然 - 如果 Buffer 接受一个类型参数,那么每次编写 Buffer&lt;thing&gt; thing 时都必须是一个类型。

    偏特化的模板参数在偏特化匹配过程中总是推导出来的;你永远不能明确地指定它们。

    但是,函数模板可以重载。所以你可以编写重载make_buffers 并使用auto

    template<class T> Buffer<sizeof(T)> make_buffer() { return {}; }
    template<size_t Size> Buffer<Size> make_buffer() { return {}; }
    
    auto buffer1 = make_buffer<int>();
    auto buffer2 = make_buffer<42>();
    

    您可以使用decltype 来实现统一但受折磨的语法:

    decltype(make_buffer<int>()) buffer1;
    decltype(make_buffer<42>()) buffer2;
    

    然后您可以轻松地为此编写一个宏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多