Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将基于模板的私有实现桥接到非模板化公共API 我想利用C++模板来进行代码重用和类型安全,但我一直在API边缘发现一些在基于模板的实现和基于运行时数据的外部接口之间进行转换的笨拙的东西。我想知道是否有办法让编译器来帮助我完成一些转换,也就是说,为我做一些这方面的工作 让我们考虑一个设计的例子,我们想对图像做一些操作,比如把它转换成另一个颜色空间。假设我们有一些任意的图像类: struct Image { /* Whatever */ };_C++_Templates_C++11_Template Meta Programming - Fatal编程技术网

将基于模板的私有实现桥接到非模板化公共API 我想利用C++模板来进行代码重用和类型安全,但我一直在API边缘发现一些在基于模板的实现和基于运行时数据的外部接口之间进行转换的笨拙的东西。我想知道是否有办法让编译器来帮助我完成一些转换,也就是说,为我做一些这方面的工作 让我们考虑一个设计的例子,我们想对图像做一些操作,比如把它转换成另一个颜色空间。假设我们有一些任意的图像类: struct Image { /* Whatever */ };

将基于模板的私有实现桥接到非模板化公共API 我想利用C++模板来进行代码重用和类型安全,但我一直在API边缘发现一些在基于模板的实现和基于运行时数据的外部接口之间进行转换的笨拙的东西。我想知道是否有办法让编译器来帮助我完成一些转换,也就是说,为我做一些这方面的工作 让我们考虑一个设计的例子,我们想对图像做一些操作,比如把它转换成另一个颜色空间。假设我们有一些任意的图像类: struct Image { /* Whatever */ };,c++,templates,c++11,template-meta-programming,C++,Templates,C++11,Template Meta Programming,然后我们有一个我们支持的转换类型的枚举: enum class ImageType : uint8_t { RGB, CMYK, Grayscale }; 然后,我们有一些私有实现,它们被模板化以重用代码,等等: // Internal implementation template <ImageType T> struct ImageConverter { public: Image ConvertImage(const Image& im

然后我们有一个我们支持的转换类型的枚举:

enum class ImageType : uint8_t {
    RGB,
    CMYK,
    Grayscale
};
然后,我们有一些私有实现,它们被模板化以重用代码,等等:

// Internal implementation
template <ImageType T>
struct ImageConverter {
public:
    Image ConvertImage(const Image& img);
private:
    void some_shared_code(Image& img) {
        // do stuff...
    };
};
然后我们对每种类型都有一些方法实例化。请注意,对于调用者来说,它们都共享相同的返回类型和参数列表

template <> Image ImageConverter<ImageType::RGB>::ConvertImage(const Image& img)
{
    Image foo = img;
    some_shared_code(foo);
    // do other stuff specific to this color space...
    return foo;
};

template <> Image ImageConverter<ImageType::CMYK>::ConvertImage(const Image& img)
{
    Image foo = img;
    some_shared_code(foo);
    // do other stuff specific to this color space...
    return foo;
};

template <> Image ImageConverter<ImageType::Grayscale>::ConvertImage(const Image& img)
{
    Image foo = img;
    some_shared_code(foo);
    // do other stuff specific to this color space...
    return foo;
};
最后,我们希望将其作为一个非模板API向外部世界出售,如下所示:

Image ConvertImage(const Image& inImage, ImageType toType) {
    switch (toType) {
        case ImageType::RGB: {
            ImageConverter<ImageType::RGB> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::CMYK: {
            ImageConverter<ImageType::CMYK> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::Grayscale: {
            ImageConverter<ImageType::Grayscale> ic;
            return ic.ConvertImage(inImage);
        }
    }
};
struct ImageConverter {
public:
  template <typename ConvT>
  Image ConvertImage(const Image& img, ConvT conv);
private:
  void some_shared_code(Image& img) {
    // do stuff...
  };
};
ConvertImage(Image(), RGBConv());
ConvertImage(Image(), GrayscaleConv());
Image x = Image(ImageType::RGB);
Image y = ConvertImage(ImageType::Grayscale, x);

if (x.img_type == y.img_type)
{
    cout << "not converted\n";
}
else
{
    cout << "converted\n";
}
这是最后一部分困扰着我——它又丑又笨重。值得一提的是,这显然是一个人为的示例,为了简洁起见使用了非类型模板参数,但问题存在于抽象中,即当模板参数是类型时

我知道声明一个纯虚拟接口类的模式,所有模板实例化都从该类继承,但这要求模板实例化从接口类继承。在使用第三方类时,有时这并不是一个真正的选项。它还有其他缺点,例如更改内存中的布局等

是否有一些习惯用法,在抽象空间中工作,可以更优雅地填充这个switch语句的角色,但不需要对实现进行繁重的更改,例如从非模板接口类继承?我觉得这一定是一个常见的问题,可能有一些聪明的解决方案,我目前的模板无法解决


编辑:我越想这个问题,我就越开始认为答案可能是基于模板元编程的,因为模板元编程是任何问题的答案。

我想到的一个可能的解决方案是使用类型标记而不是枚举:

struct RGBConv{};
struct CMYKConv{};
struct GrayscaleConv{};  
然后您可以这样声明ImageConverter:

Image ConvertImage(const Image& inImage, ImageType toType) {
    switch (toType) {
        case ImageType::RGB: {
            ImageConverter<ImageType::RGB> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::CMYK: {
            ImageConverter<ImageType::CMYK> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::Grayscale: {
            ImageConverter<ImageType::Grayscale> ic;
            return ic.ConvertImage(inImage);
        }
    }
};
struct ImageConverter {
public:
  template <typename ConvT>
  Image ConvertImage(const Image& img, ConvT conv);
private:
  void some_shared_code(Image& img) {
    // do stuff...
  };
};
ConvertImage(Image(), RGBConv());
ConvertImage(Image(), GrayscaleConv());
Image x = Image(ImageType::RGB);
Image y = ConvertImage(ImageType::Grayscale, x);

if (x.img_type == y.img_type)
{
    cout << "not converted\n";
}
else
{
    cout << "converted\n";
}
现在我们可以去掉switch语句:

template <typename T>
Image ConvertImage(const Image& inImage, T conv) {
  return ImageConverter().ConvertImage(inImage, conv);
};

我提出了一些类似的方法,但我认为一些模板元编程向导将不得不考虑是否有更好的方法。以下是我的想法:

首先是一些模型的东西:

enum class ImageType : uint8_t {
    RGB,
    CMYK,
    Grayscale,
    Invalid
};

struct Image {
    Image(ImageType type) : img_type(type) {};

    ImageType img_type;
    // other stuff...
};
然后我们的模板转换器类

template <ImageType T>
struct ImageConverter {
public:
    Image ConvertImage(const Image& img);
private:
    void some_shared_code(Image& img) {
        // do stuff...
    };
};
然后在非模板API中,我使用这些递归模板,以及各种选项的列表,并将要匹配的运行时数据值和输入图像传递给它

Image ConvertImage(ImageType desiredType, const Image& inImage)
{
    return _maker<ImageType::RGB, ImageType::CMYK, ImageType::Grayscale, ImageType::Invalid>()(desiredType, inImage);
};
这样称呼:

Image ConvertImage(const Image& inImage, ImageType toType) {
    switch (toType) {
        case ImageType::RGB: {
            ImageConverter<ImageType::RGB> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::CMYK: {
            ImageConverter<ImageType::CMYK> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::Grayscale: {
            ImageConverter<ImageType::Grayscale> ic;
            return ic.ConvertImage(inImage);
        }
    }
};
struct ImageConverter {
public:
  template <typename ConvT>
  Image ConvertImage(const Image& img, ConvT conv);
private:
  void some_shared_code(Image& img) {
    // do stuff...
  };
};
ConvertImage(Image(), RGBConv());
ConvertImage(Image(), GrayscaleConv());
Image x = Image(ImageType::RGB);
Image y = ConvertImage(ImageType::Grayscale, x);

if (x.img_type == y.img_type)
{
    cout << "not converted\n";
}
else
{
    cout << "converted\n";
}
在最深处,我们得到的是这样的回溯:

Image ConvertImage(const Image& inImage, ImageType toType) {
    switch (toType) {
        case ImageType::RGB: {
            ImageConverter<ImageType::RGB> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::CMYK: {
            ImageConverter<ImageType::CMYK> ic;
            return ic.ConvertImage(inImage);
        }
        case ImageType::Grayscale: {
            ImageConverter<ImageType::Grayscale> ic;
            return ic.ConvertImage(inImage);
        }
    }
};
struct ImageConverter {
public:
  template <typename ConvT>
  Image ConvertImage(const Image& img, ConvT conv);
private:
  void some_shared_code(Image& img) {
    // do stuff...
  };
};
ConvertImage(Image(), RGBConv());
ConvertImage(Image(), GrayscaleConv());
Image x = Image(ImageType::RGB);
Image y = ConvertImage(ImageType::Grayscale, x);

if (x.img_type == y.img_type)
{
    cout << "not converted\n";
}
else
{
    cout << "converted\n";
}
这基本上达到了我的目标,摆脱了switch语句。如果它更干净一点,也就是说,我不需要跳转到递归的底部,或者如果它不是递归的,那就更好了。但是这个C++11可变模板的东西已经给我带来了很大的压力


希望一些熟练的模板元程序员有更好的想法。

从可读性的角度来看,这更好,但将API函数模板化偏离了问题的基本点。它没有在运行时数据和编译时类型之间进行转换,而是将模板输出到API中。