从一个模板实例转换为另一个模板实例

时间:2015-10-08 09:39:10

标签: c++11 template-meta-programming

我有一个固定大小的字符串类,定义如下:

template <typename const std::size_t max_size>
class fixed_string {
...
};

此类保留一个char缓冲区来保存max_size字符。

我希望能够将此类的对象传递给采用模板实例化的方法,该模板实例化具有较低的max_size模板参数值而无需任何复制。例如,以下方法:

void foo(const fixed_string<50>& val) {
}

应该使用fixed_string&lt; 100&gt;来调用,以便以下操作:

void bar() {
 fixed_string<100> s = "gg";
 foo(s); 
}

我怎么能这样做?

4 个答案:

答案 0 :(得分:1)

假设编译器以类似的方式将它们排出,那么第一直觉是reinterpret_cast从大到小fixed_string,但由于C ++的严格别名规则,这将是非法的(对象可能永远不会被不同类型的引用或指针访问,除非其中一个是[signed|unsigned] char

您可以通过创建fixed_string可以隐式转换为{1}的自定义参考模板来解决此问题:

#include <type_traits>

template<std::size_t N>
struct fixed_string_ref {
    char *string; 
};

template<std::size_t N>
struct fixed_string_const_ref {
    const char *string; 
};

template<std::size_t N>
struct fixed_string {
    char string[N];

    template<std::size_t M, typename std::enable_if<(M < N), int>::type = 0>
    operator fixed_string_const_ref<M>() const {
        return fixed_string_const_ref<M> { string };
    }

    template<std::size_t M, typename std::enable_if<(M < N), int>::type = 0>
    operator fixed_string_ref<M>() {
        return fixed_string_ref<M> { string };
    }
};

void foo(fixed_string_const_ref<10>) {}

int main() {
    fixed_string<20> f;
    foo(f);
  //fixed_string<5> g;
  //foo(g);              <- cannot be converted to fixed_string_const_ref<10>
}

fixed_string[_const]_ref只保存一个指向fixed_string数据的指针,因此可以通过值传递而不复制字符串。需要有两个这样的类型,一个是const,一个是非const,以保持const正确性。

enable_if部分确保只能创建对小于或等于fixed_string的引用。

可选:通过向fixed_string_const_ref添加两个构造函数,普通字符串文字也可以作为固定字符串引用传递。

template<std::size_t N>
struct fixed_string_const_ref {
    const char *string;

    explicit fixed_string_const_ref(const char *s)
        : string(s) {}

    template<std::size_t M, typename std::enable_if<(M >= N), int>::type = 0>
    fixed_string_const_ref(const char (&s)[M])
        : string(s) {}
};

// ...

int main() {
    // ....
    foo("Hello World");
  //foo("Hello");        <- cannot be converted to fixed_string_const_ref<10>
}

通过explicit制作第一个enable_if和第二个{{1}},只能通过足够长的字符串文字创建引用。

答案 1 :(得分:0)

fixed_string<50>fixed_string<100>是不相关的类型,因此默认情况下不起作用。

一种可能性是fixed_string<N>继承fixed_string<N-1>,但这可能会对您的实施造成不必要的限制。

您可以将foo作为功能模板并使用static_assert或SFINAE来确保传递正确大小的字符串:

template <std::size_t Size>
void foo (const fixed_string<Size>& val) {
    static_assert(Size >= 50, "Size must be at least 50");
}

答案 2 :(得分:0)

首先:模板的两个不同实例是不同的类型,完全不相关。与将floatchar*提供给完全无法使用的相同方法的问题相同。

因为固定大小的容器类型可以在底层使用相同的方法,所以您可以从基类派生它作为选项。

template <typename const std::size_t max_size>
     class fixed_string: public fixed_string_base {...}

然后,这个基类可以保存所有通过虚函数与容器内容交互的方法。

但这似乎是一个设计问题!也许作为一个提示: 如果您在编译时决定容器的大小,但程序的其余部分必须在运行时使用不同类型,则您没有真正的使用好处。也许你的基类有像getSize()这样的方法,它们在每个模板实例中被覆盖,你有与运行时动态数组大小相同的内存/时间成本结果。

答案 3 :(得分:0)

也许您可以在课程中添加复制构造函数,如下所示:

template <int max_size>
struct fixed_string {
   template <int sz> fixed_string(const fixed_string<sz> &f) {}
};

然后,您可以将fixed_string<100>传递给获取const fixed_string<50>&的功能,但是:

  1. 它将创建fixed_string<50>的新实例。一旦不复制数据,这可能并不算太糟糕。因此,在您的类中,您可以拥有静态缓冲区和对缓冲区的引用。默认情况下,引用指向内部缓冲区,但也可以引用其他实例的缓冲区。
  2. 可能仍需要使用enable_if来确保尺寸合适
  3. 实现了缓冲区引用分离后,您可以使整个事物更加隐式,并创建一种特殊类型的构造函数,它将与常规复制构造函数(您可能需要它)区分开来接受较小而不是较大的缓冲区)