jjzjj

c++ - 惯用的 C++11 类型提升

coder 2023-06-03 原文

a great paper在用于科学计算的 C++ 上,作者 (T. Veldhuizen) 提出了一种基于特征的 方法来解决类型提升问题。我曾经使用过这种方法,并且发现它很有效:

#include<iostream>
#include<complex>
#include<typeinfo>

template<typename T1, typename T2>
struct promote_trait{};

#define DECLARE_PROMOTION(A, B, C) template<> struct promote_trait<A, B> { using T_promote = C;};

DECLARE_PROMOTION(int, char, int);
DECLARE_PROMOTION(int, float, float);
DECLARE_PROMOTION(float, std::complex<float>, std::complex<float>);

// similarly for all possible type combinations...

template<typename T1, typename T2>
void product(T1 a, T2 b) {
  using T = typename promote_trait<T1, T2>::T_promote;
  T ans = T(a) * T(b);  
  std::cout<<"received "
           <<typeid(T1).name()<<"("<<a<<")"<<" * "
           <<typeid(T2).name()<<"("<<b<<")"<<" ==> "
           <<"returning "
           <<typeid(T).name()<<"("<<ans<<")"<<std::endl;
}

int main() {
  product(1, 'a');
  product(1, 2.0f);
  product(1.0f, std::complex<float>(1.0f, 2.0f));
  return 0;
}

输出:

received i(1) * c(a) ==> returning i(97)
received i(1) * f(2) ==> returning f(2)
received f(1) * St7complexIfE((1,2)) ==> returning St7complexIfE((1,2))

typeinfo 返回的类型名称取决于实现;您的输出可能与我的不同,后者在 OS X 10.7.4 上使用 GCC 4.7.2

本质上,该方法定义了 promote_trait它仅包含一个类型定义:以给定方式操作时应将两种类型提升到的类型。需要声明所有可能的促销事件。

当一个函数同时接收两种类型时,它依赖于 promote_trait推断出正确的、提升的结果类型。如果没有为给定对定义特征,则代码无法编译(一个理想的特性)。

现在,有问题的论文写于 2000 年,我们知道 C++ 在过去十年中发生了巨大的变化。那么,我的问题如下:

是否有一种现代的、惯用的 C++ 11 方法来处理与 Veldhuizen 引入的基于特征的方法一样有效的类型提升?

编辑(使用 std::common_type)

根据 Luc Danton 的建议,我创建了以下使用 std::common_type 的代码:

#include<iostream>
#include<complex>
#include<typeinfo>
#include<typeindex>
#include<string>
#include<utility>
#include<map>

// a map to homogenize the type names across platforms
std::map<std::type_index, std::string> type_names = {
  {typeid(char)                 , "char"},  
  {typeid(int)                  , "int"},
  {typeid(float)                , "float"},
  {typeid(double)               , "double"},
  {typeid(std::complex<int>)    , "complex<int>"},
  {typeid(std::complex<float>)  , "complex<float>"},
  {typeid(std::complex<double>) , "complex<double>"},
};

template<typename T1, typename T2>
void promotion(T1 a, T2 b) {
  std::string T1name = type_names[typeid(T1)];
  std::string T2name = type_names[typeid(T2)];
  std::string TPname = type_names[typeid(typename std::common_type<T1, T2>::type)];  
  std::cout<<T1name<<"("<<a<<") and "<<T2name<<"("<<b<<") promoted to "<<TPname<<std::endl;
}

int main() {
  promotion(1, 'a');
  promotion(1, 1.0);
  promotion(1.0, 1);
  promotion(std::complex<double>(1), 1);
  promotion(1.0f, 1);
  promotion(1.0f, 1.0);
  promotion(std::complex<int>(1), std::complex<double>(1));
  promotion(std::complex<double>(1), std::complex<int>(1));
  promotion(std::complex<float>(0, 2.0f), std::complex<int>(1));

  return 0;
}

带输出:

int(1) and char(a) promoted to int
int(1) and double(1) promoted to double
double(1) and int(1) promoted to double
complex<double>((1,0)) and int(1) promoted to complex<double>
float(1) and int(1) promoted to float
float(1) and double(1) promoted to double
complex<int>((1,0)) and complex<double>((1,0)) promoted to complex<int>
complex<double>((1,0)) and complex<int>((1,0)) promoted to complex<int>
complex<float>((0,2)) and complex<int>((1,0)) promoted to complex<int>

我惊讶地发现,除了最后三个促销事件之外,所有促销事件都符合我的预期。为什么会complex<int>complex<double>complex<float>晋升为complex<int> !?

最佳答案

就像 catcradle 的答案一样,decltypecommon_type(以及它的自定义特化)可能是很好的 C++11 替代品,可以满足 Veldhuizen 具有的转换特征的需求心里。但是,如果您仍然需要非常特定于评估将两种类型映射为一种(二元运算符)的函数,它仍然会有所不足。 (换句话说 decltype 不知道你的问题的数学领域)。

我认为您可以使用 Boost.MPL 映射 http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html ,这甚至不需要C++11,只是当时没有写MPL:

#include<iostream>
#include<complex>
#include<typeinfo>
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
// all traits in one place, no need for MACROS or C++11, compile error if the case does not exist.
using namespace boost::mpl;
typedef map<
    pair<pair<int, char>, int>,
    pair<pair<int, float>, int>,
    pair<pair<float, std::complex<float> >, std::complex<float> >
> mapped_promotion;

template<typename T1, typename T2>
void product(T1 a, T2 b) {
  typedef typename at<mapped_promotion, pair<T1, T2> >::type T;

  T ans = T(a) * T(b);  
  std::cout<<"received "
           <<typeid(T1).name()<<"("<<a<<")"<<" * "
           <<typeid(T2).name()<<"("<<b<<")"<<" ==> "
           <<"returning "
           <<typeid(T).name()<<"("<<ans<<")"<<std::endl;
}

int main() {
  product(1, 'a');
  product(1, 2.0f);
  product(1.0f, std::complex<float>(1.0f, 2.0f));
  return 0;
}

使用 MPL 的另一个额外好处是,您以后可以轻松地迁移到 Boost.Fusion,这通常是您开始处理类型的“代数”时的情况。而且C++11核心语言中Boost.Fusion的功能也没有什么可以替代的。


下面是一个更通用的解决方案,如果以上内容对于您的应用程序来说已经足够了,您可以停止阅读,它结合了 MPL 和 decltype 并且需要 C++11,它允许未指定的类型对默认为 decltype 解决方案,如果它有任何好处,诀窍是查看 mpl::map 的返回是否是元类型 void_(未找到对)。

...
#include <type_traits> 
//specific promotions
using namespace boost::mpl;
typedef map<
    pair<pair<int, char>, int>,
    pair<pair<int, float>, int>,
    pair<pair<float, std::complex<float> >, std::complex<float> >
> specific_mapped_promotion;

//promotion for unspecified combinations defaults to decltype type deduction.
template<class P1, class P2>
struct loose_mapped_promotion : std::conditional<
    std::is_same<typename at<specific_mapped_promotion, pair<P1, P2> >::type, mpl_::void_>::value,
    decltype( std::declval<P1>()*std::declval<P2>() ),
    typename at<specific_mapped_promotion, pair<P1, P2> >::type
> {};
template<typename T1, typename T2>
void product(T1 a, T2 b) {
  typedef typename loose_mapped_promotion<T1, T2>::type T;

  T ans = T(a) * T(b);
  ...
}
int main() {
   product(1.0, std::complex<double>(1.0f, 2.0f)); // now accepted, although no explicit trait was made
}

最后一点:对于特殊情况,重载 std::common_type 显然是可以的,如果你想使用它:http://www.cplusplus.com/reference/type_traits/common_type/

关于c++ - 惯用的 C++11 类型提升,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16865376/

有关c++ - 惯用的 C++11 类型提升的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  3. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  4. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  5. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  6. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

    运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. ruby - 在 Ruby 中是否有一种惯用的方法来操作 2 个数组? - 2

    a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],

随机推荐