有一个科学计算的a great paper on 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返回的类型的名称取决于实现;您的输出可能与我的不同,后者在OSX10.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>!?
发布于 2013-05-31 20:58:05
就像在“悬挂式回答”中一样,decltype和common_type (以及它的自定义专门化)可能是很好的C++11替代品,以满足Veldhuizen对转换特性的需求。但是,如果您仍然需要非常具体地计算将两种类型映射为一种类型(二进制运算符)的函数,那么它仍然是不够的。(换句话说,decltype不知道问题的数学域)。
我的观点是,您可以求助于Boost.MPL地图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,这通常是当您开始处理类型的“代数”时的情况。在Boost.Fusion核心语言中,没有什么可以取代C++11的功能。
下面是一个更通用的解决方案,您可以停止阅读上面的内容,如果上面的内容足够您的应用程序,它结合了MPL和decltype,并要求C++11 (如果它有任何好处,允许未指定的一对类型默认为解密类型解决方案),诀窍是查看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:type/,那么在特殊情况下超载显然是可以的。
发布于 2013-05-31 20:29:30
我认为您可以使用decltype进行如下操作:
template <typename T, typename U>
void product(T t, U u)
{
std::cout << typeid(decltype(t * u)).name() << std::endl;
}或使用declval
#include <utility>
template <typename T, typename U>
void product()
{
std::cout << typeid(decltype(std::declval<T>() * std::declval<U>())).name() << std::endl;
}编辑 For T ans = T(a) * T(b);您只需使用auto,auto ans = T(a) * T(b);
https://stackoverflow.com/questions/16865376
复制相似问题