根据数据返回不同的数据类型(C++)

Posted

技术标签:

【中文标题】根据数据返回不同的数据类型(C++)【英文标题】:Returning different data type depending on the data (C++) 【发布时间】:2009-05-31 00:23:53 【问题描述】:

有没有办法做这样的事情?

(correct pointer datatype) returnPointer(void* ptr, int depth)


    if(depth == 8)
        return (uint8*)ptr;
    else if (depth == 16)
        return (uint16*)ptr;
    else
        return (uint32*)ptr;

谢谢

【问题讨论】:

你希望如何使用这个?您的设计可能没有遵循最佳 OOP 实践。 我使用的是 Adob​​e Photoshop SDK,他们使用的是 void* 您能否给出一个使用示例,包括调用 Photoshop SDK 函数? 【参考方案1】:

没有。 C++ 函数的返回类型只能根据显式模板参数或其参数的类型而有所不同。它不能根据其参数的而变化。

但是,您可以使用各种技术来创建一个由其他几种类型组合而成的类型。不幸的是,这不一定对您有帮助,因为其中一种技术是 void * 本身,并且回到原来的类型会很痛苦。

但是,通过彻底解决问题,您可能会得到想要的结果。我想你会想使用你发布的代码,例如:

void bitmap_operation(void *data, int depth, int width, int height) 
  some_magical_type p_pixels = returnPointer(data, depth);
  for (int x = 0; x < width; x++)
    for (int y = 0; y < width; y++)
      p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]);

因为 C++ 需要在编译时知道 p_pixels 的类型,所以这不会按原样工作。但是我们可以做的是让bitmap_operation本身成为一个模板,然后用一个基于深度的开关来包裹它:

template<typename PixelType>
void bitmap_operation_impl(void *data, int width, int height) 
  PixelType *p_pixels = (PixelType *)data;
  for (int x = 0; x < width; x++)
    for (int y = 0; y < width; y++)
      p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]);


void bitmap_operation(void *data, int depth, int width, int height) 
  if (depth == 8)
    bitmap_operation_impl<uint8_t>(data, width, height);
  else if (depth == 16)
    bitmap_operation_impl<uint16_t>(data, width, height);
  else if (depth == 32)
    bitmap_operation_impl<uint32_t>(data, width, height);
  else assert(!"Impossible depth!");

现在编译器会自动为你生成bitmap_operation_impl的三个实现。

【讨论】:

在 C# 中,您可以返回一个对象类型,并将其强制转换为您想要的对象(前提是您知道该类型)。或者你可以使用泛型。你能用 c++ 做类似的事情吗? 最接近 C++ 的对象类型是 void *。还有 boost 的变体类型。 我想我也会使用 boost 变体。【参考方案2】:

如果您可以使用模板参数而不是普通参数,则可以创建一个模板函数,该函数为每个depth 值返回正确的类型。首先需要根据depth 定义正确的类型。您可以为不同的位大小定义专门化的模板:

// template declaration
template<int depth>
struct uint_tmpl;

// specializations for certain types
template<> struct uint_tmpl<8>   typedef uint8_t type; ;
template<> struct uint_tmpl<16>  typedef uint16_t type; ;
template<> struct uint_tmpl<32>  typedef uint32_t type; ;

此定义可用于声明一个模板化函数,该函数为每个位值返回正确的类型:

// generic declaration
template<int depth>
typename uint_tmpl<depth>::type* returnPointer(void* ptr);

// specializations for different depths
template<> uint8_t*  returnPointer<8>(void* ptr)   return (uint8_t*)ptr;  
template<> uint16_t* returnPointer<16>(void* ptr)  return (uint16_t*)ptr; 
template<> uint32_t* returnPointer<32>(void* ptr)  return (uint32_t*)ptr; 

【讨论】:

与没有模板的解决方案(如 returnPointer8()、returnPointer16() 和 returnPointer32())相比,这没有任何优势,因为模板参数必须是常量(即程序员知道的)。 在编译时必须知道模板参数是模板的一般属性。这仍然比不同命名的函数更灵活,例如,您可以定义类似 template ptradd(void* ptr, int val) *returnPointer(ptr) += val; 。这适用于所有深度。使用不同命名的 returnPointer 函数,您还需要声明几个单独的 ptradd 变体。对于模板版本,一个 ptradd 函数就足够了。【参考方案3】:

您可以在堆上分配一些内存,并返回一个您强制转换为已分配类型的 void*。这是一种危险且不安全的工作方式,是一种古老的 C 技巧。

您可以返回一个包含所有有效数据类型(和一个选择指示符)的联合。

您可以使用模板,这是针对这类事情推荐的 C++ 方式。

您可以提供一组重载函数,这些函数将(每种类型的)参数作为引用 - 编译器将根据数据类型决定调用哪个函数。我经常喜欢这种方式,因为我认为它最简单。

【讨论】:

【参考方案4】:

没有;你不能在 C++ 中做到这一点。正确答案是返回void *

从调用的另一端考虑它——从编译器的角度考虑:

如果编译器不可能知道将返回三种返回类型中的哪一种,它如何能够验证返回值是否被正确使用(例如分配给正确类型的变量)?

此时,将“多种类型之一”分配给返回值的概念变得毫无意义。函数的返回类型在生活中没有其他目的,只是让编译器能够完成它的工作。编译器需要“一个”类型才能进行类型检查。由于您直到运行时才知道它是哪一个,因此编译器无法为您进行类型检查。您必须告诉编译器“停止尝试”以将返回值与任何特定的指针类型相匹配——因此,返回 void *

如果您的深度参数在编译时已知,您可以选择使用一组模板,如 @sth 演示的,或使用一组单独的独立函数,或使用一组调用共享实现函数的相关函数和然后将返回值转换为正确的类型。你选择哪一个主要是一个审美决定。

如果depth 的值直到运行时才知道,那么您可能应该返回void *

现在,我假设您的实际实现实际上做了一些事情来生成指针,而不是您的示例代码显示的内容。您的示例代码不是实际功能;这更像是试图复制 cast 所做的事情。 cast 不是函数调用;这是一个编译器指令,试图将它的操作数“制作”为特定类型(确切地说是“如何”,对于另一篇文章来说是一个长篇大论)。它不是 C++ 语言操作,而是编译器操作。你不能用 C++ 本身重写它。

【讨论】:

以上是关于根据数据返回不同的数据类型(C++)的主要内容,如果未能解决你的问题,请参考以下文章

C ++函数返回不同的数据类型[重复]

C++:仅根据函数返回值类型不能实现重载

逆向基础 C++ Primer Plus 第三章 处理数据

C++基本数据类型的字节数范围大小溢出处理

C++基本数据类型的字节数与范围大小

如何让单个接口返回不同的数据类型?