关于MSVC和c++如何处理c++模板代码的差异
Regarding differences between how MSVC and g++ treat C++ template code
我在ACCU (http://accu.org/index.php/journals/1916)上偶然发现了这段代码,并且对通过使用CRTP在c++中应用AOP来完成一个有趣的项目很感兴趣。作者给出的代码如下:
namespace aop
{
template <class A>
class NullAspect
{};
template <template <template <class> class> class Base>
struct Decorate
{
private:
struct None {};
template <template <class> class ... Aspects>
struct Apply;
template <template <class> class T>
struct Apply<T>
{
template <class E>
using Type = T<E>;
};
template<template < class > class A1, template < class > class ... Aspects>
struct Apply<A1, Aspects...>
{
template <class T>
using Type = A1<typename Apply<Aspects...>::template Type<T>>; // the errors point to this line and the 'Type' refers to the 'template Type<T>'
};
public:
template<template <class> class ... Aspects>
struct with
{
template <class T>
using AspectsCombination = typename Apply<Aspects...>::template Type<T>;
typedef AspectsCombination<Base<AspectsCombination>> Type;
};
};
}
我尝试在Microsoft VS2015中编译它,它给了我以下一组错误:
Error C2146 syntax error: missing '>' before identifier 'Type'
Error C2947 expecting '>' to terminate template-argument-list, found '<'
Error C2061 syntax error: identifier 'T'
Error C2238 unexpected token(s) preceding ';'
Error C1201 unable to continue after syntax error in class template definition
Error C2143 syntax error: missing ';' before '}'
Error C2238 unexpected token(s) preceding ';'
我使用了相同的代码,检查了语法并在g++中编译,它编译得很好。我应该注意的两个编译器之间是否存在任何差异?这里的问题是什么,使cl.exe生成这些错误?它们是由于cl.exe如何解析任何基于模板的代码?需要做哪些更改才能使此代码在msvc上工作?
编辑:这里是作者提供的test.cpp的完整代码,以帮助您更清楚地了解:
#include <iostream>
#include <cmath>
#include "aop.h"
//#define INHERITING_CTORS as of g++ 6.4.3, inheriting ctors was not implemented
template <typename _UnderlyingType>
struct Number
{
template <template <class> class A = aop::NullAspect>
class Type
{
public:
typedef _UnderlyingType UnderlyingType;
typedef A<Number::Type<A>> FullType;
Type(UnderlyingType n)
: n(n)
{}
friend std::ostream& operator<<(std::ostream& out, const Type& number)
{
return out << number.n;
}
protected:
UnderlyingType n;
};
};
template <class A>
class ArithmeticAspect: public A
{
public:
typedef typename A::FullType FullType;
#ifdef INHERITING_CTORS
using A::A;
#else
ArithmeticAspect(typename A::UnderlyingType n)
: A(n)
{}
ArithmeticAspect(const A& a)
: A(a)
{}
#endif
FullType operator+(const FullType& other) const
{
FullType tmp(*this);
return tmp += other;
}
FullType operator-(const FullType& other) const
{
FullType tmp(*this);
return tmp -= other;
}
FullType operator+=(const FullType& other)
{
A::n += other.n;
return A::n;
}
FullType operator-=(const FullType& other)
{
A::n -= other.n;
return A::n;
}
// same for *, *=, /, /=
};
template <class A>
class IncrementalAspect: public A
{
public:
typedef typename A::FullType FullType;
#ifdef INHERITING_CTORS
using A::A;
#else
IncrementalAspect(typename A::UnderlyingType n)
: A(n)
{}
IncrementalAspect(const A& a)
: A(a)
{}
#endif
FullType operator++(int)
{
FullType tmp(*this);
operator++();
return tmp;
}
FullType operator++()
{
++A::n;
return *this;
}
FullType operator--(int)
{
FullType tmp(*this);
operator--();
return tmp;
}
FullType operator--()
{
--A::n;
return *this;
}
};
/*
* Configurable Aspect sumExample
*/
template <unsigned int PRECISION>
struct RoundAspect
{
template <class A>
class Type : public A
{
public:
typedef typename A::FullType FullType;
#ifdef INHERITING_CTORS
using A::A;
#else
Type(typename A::UnderlyingType n)
: A(n)
{}
Type(const A& a)
: A(a)
{}
#endif
FullType operator+(const FullType& other) const
{
return FullType(round(A::operator+(other).n));
}
private:
static float round(float f)
{
const unsigned int e = std::pow(10, PRECISION);
return float(int(f * e)) / e;
}
};
};
template <class A>
class LogicalAspect: public A
{
public:
typedef typename A::FullType FullType;
#ifdef INHERITING_CTORS
using A::A;
#else
LogicalAspect(typename A::UnderlyingType n)
: A(n)
{}
LogicalAspect(const A& a)
: A(a)
{}
#endif
bool operator!() const
{
return !A::n;
}
bool operator&&(const FullType& other) const
{
return A::n && other.n;
}
bool operator||(const FullType& other) const
{
return A::n || other.n;
}
};
template <class A>
class BitwiseAspect: public A
{
public:
typedef typename A::FullType FullType;
#ifdef INHERITING_CTORS
using A::A;
#else
BitwiseAspect(typename A::UnderlyingType n)
: A(n)
{}
BitwiseAspect(const A& a)
: A(a)
{}
#endif
bool operator~() const
{
return ~A::n;
}
FullType operator&(const FullType& mask) const
{
return A::n & mask.n;
}
FullType operator|(const FullType& mask) const
{
return A::n | mask.n;
}
FullType operator<<(const FullType& bitcount) const
{
return A::n << bitcount.n;
}
FullType operator>>(const FullType& bitcount) const
{
return A::n >> bitcount.n;
}
FullType& operator>>=(const FullType& bitcount)
{
A::n >>= bitcount.n;
return *static_cast<FullType*>(this);
}
};
template <class N>
void sumExample(typename N::UnderlyingType n1, typename N::UnderlyingType n2)
{
N a(n1);
N b(n2);
N c = a + b;
std::cout << c << std::endl;
}
template <class N>
void orExample(typename N::UnderlyingType n1, typename N::UnderlyingType n2)
{
N a(n1);
N b(n2);
std::cout << (a || b) << std::endl;
}
template <class N>
void bitwiseExample(typename N::UnderlyingType n1, typename N::UnderlyingType n2)
{
N a(n1);
N b(n2);
std::cout << (a + ((b >>= 1) << 3)) << std::endl;
}
int main()
{
typedef aop::Decorate<Number<unsigned int>::Type>::with<ArithmeticAspect, IncrementalAspect, LogicalAspect, BitwiseAspect>::Type IntegralNumber;
bitwiseExample<IntegralNumber>(1, 2);
sumExample<IntegralNumber>(1, 2);
typedef aop::Decorate<Number<float>::Type>::with<RoundAspect<2>::Type, ArithmeticAspect, LogicalAspect>::Type FloatRoundLogicalNumber;
orExample<FloatRoundLogicalNumber>(1, 0);
typedef aop::Decorate<Number<int>::Type>::with<LogicalAspect>::Type IntLogicalNumber;
orExample<IntLogicalNumber>(1, 0);
typedef aop::Decorate<Number<float>::Type>::with<RoundAspect<2>::Type, ArithmeticAspect>::Type FloatRoundNumber;
sumExample<FloatRoundNumber>(1.339, 1.1233);
return 0;
}
IMHO这是模板化模板上的>>
综合征。在c++ 11之前,需要一个空格来分隔>
令牌,而不是一个>>
令牌。
从c++ 11 n4296草案开始,在14.2中说模板特化的名称[temp.names]§3:
…类似地,第一个非嵌套的>>被视为两个连续的但是不同的>标记,其中第一个作为模板参数列表的结束,并完成模板id .
看起来MSVC2015还没有实现这部分标准(或者你可能忘记声明源代码的c++版本*)
为了完整起见,当使用CLang 3.4.1编译而不指定std=c++11
时,会显示以下错误:
error: a space is required between consecutive right angle brackets (use '> >') using Type = A1<typename Apply<Aspects...>::template Type<T>>; // the er...
并且希望在c++ 11模式下甚至没有警告…
(*)不幸的是,我无法访问VS2015,微软在他们的c++ 11/14/17功能页面中声明,右尖括号是由VS2013和2015实现的。所以我假设在项目属性中应该有一个代码级别指示。
所以,在摆弄了几遍,读了几遍之后,我终于给原作者发了一封电子邮件。所以所有的功劳都归他,Hugo Arregui先生。
对于cl抱怨的那行,修复方法如下:
template < template <class> class A1, template <class> class A2, template <class> class... Aspects>
struct Apply<A1,A2,Aspects...>
{
template<class T>
using Type = A1< typename Apply<A2, Aspects...>::template Type<T> >;
};
如果有人知道为什么微软的编译器要求我们在template参数中展开至少2个模板类,并在这里给出解释,那就太好了。
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么在这个代码结束循环中没有得到结束
- 在c代码之间共享数据的最佳方式
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值