使用“非类型参数”(或反过来)专门化“类型参数”

时间:2015-01-02 12:31:25

标签: c++ templates partial-specialization

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

#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"

有没有办法让我有两个这样的模板专门化 - 一个使用显式字节大小(非类型参数),另一个使用sizeof()类型的大小(T} (类型参数)?我对一个不需要两个具有不同名称或任何#define宏的独立模板的解决方案感兴趣。

我试图在&#34;反向&#34; - 将<typename T>作为主要模板,使用size_t作为专业化(将char[sizeof(T)]std::aligned_storage<...>::type传递给主模板),但也失败了。

2 个答案:

答案 0 :(得分:3)

不,你不能。一旦我们有了template <size_t Size> 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_tT更专业。

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

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)> { };

答案 1 :(得分:2)

类模板不能重载。如果类模板Buffer采用类型为size_t的非类型参数,那么每次编写Buffer<thing>时,thing必须是有效的非类型参数;它不是一种类型。相反的情况也是如此 - 如果Buffer采用类型参数,则每次撰写Buffer<thing> thing时都必须成为类型。

部分特化的模板参数在部分特化匹配期间始​​终推导;你永远不能明确指定它们。

但是,功能模板可能会重载。因此,您可以编写重载make_buffer并使用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;

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