gia*_*uca 0 c++ templates preprocessor-directive
我正在用C++写一个(不是真的)简单的项目(我的第一个,来自普通的C).我想知道是否有办法简化具有相同模板模式的多个函数的定义.我想一个例子可以更好地解释这个问题.
假设我有一个"Set"类,它代表一个数字列表,定义为
template <class T>
class Set {
static_assert(std::is_arithmetic<T>(), "Template argument must be an arithmetic type.");
T *_address;
...
}
Run Code Online (Sandbox Code Playgroud)
所以,如果我有一个实例(比如说Set<double>)和一个数组U array[N],其中U是另一个算术类型并且N是一个整数,我希望能够执行一些操作,例如将数组的值赋给那些数组Set.因此我在类中创建了函数模板
template <class U, int N>
void assign(U (&s)[N]) {
static_assert(std::is_arithmetic<U>(), "Template argument must be an arithmetic type.");
errlog(E_BAD_ARRAY_SIZE, N == _size);
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < size);
}
Run Code Online (Sandbox Code Playgroud)
至于我的测试,上面的代码完全正常.但是我发现真的很难看,因为我需要static_assert确保只将算术类型作为参数(参数U),我需要一种方法来确保数组大小(参数N).另外,我不与完成assign的功能,但我需要这么多的其他功能,如add,multiply,scalar_product等等,等等!
我当时想知道是否有更好的方式来编写这类课程.经过一些工作,我想出了一个预处理器指令:
#define arithmetic_v(_U_, _N_, _DECL_, ...) \
template <class U, idx_t N> _DECL_ \
{ \
static_assert(std::is_arithmetic<U>(),"Rvalue is not an arithmetic type."); \
errlog(E_BAD_ARRAY_SIZE, N == _size); \
__VA_ARGS__ \
}
Run Code Online (Sandbox Code Playgroud)
从而将我的功能定义为
arithmetic_v(U, N,
void assign(U (&value)[N]),
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < _size);
)
Run Code Online (Sandbox Code Playgroud)
这在某种程度上更干净,但仍然不是最好的,因为我被迫丢失包裹函数体的括号(必须包含static_assertINSIDE函数本身,模板参数U在范围内).
我发现的解决方案似乎工作得很好,代码比以前更具可读性,但是...我不能使用另一个构造,允许我构建一个更清晰的所有函数的定义,并仍然保留该static_assert块和关于数组大小的信息?对于我需要的每个函数重复一次模板代码真的很难...
我只是想了解这种语言,因此我们非常感谢有关这一论点的任何其他信息.我尽可能多地搜索,但找不到任何东西(也许我想不出适当的关键词,要求谷歌找到相关的东西).在此先感谢您的帮助,祝大家新年快乐!
赞布罗塔
我强烈建议不要使用宏,除非没有办法解决它们(我想到的一个案例就是获取用于调试目的的行号).来自Google C++样式指南(http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preprocessor_Macros):
宏意味着您看到的代码与编译器看到的代码不同.这可能会引入意外行为,尤其是因为宏具有全局范围.
我真的不明白为什么你考虑使用static_assert丑陋.还有另一种方法可以确保模板仅适用于使用SFINAE的某些类型.
template <class T, class Enable = void>
class X;
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type> {
};
Run Code Online (Sandbox Code Playgroud)
并且你可以使用一个using声明(没有双关语)来做更漂亮的事情:
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
Run Code Online (Sandbox Code Playgroud)
现在
X<int> x; // ok, int is integral
X<float> y; // compile error
Run Code Online (Sandbox Code Playgroud)
SFINAE(替换失败不是错误)是C++中的一项功能,如果模板特化失败,则不会出现错误.
template <bool Cond, class T = void> struct enable_if.如果为true,T则启用类型作为成员类型.否则,没有定义.因此对于float类型是false并且不存在,所以模板专门化enable_if::typeCondenable_if::typeis_integralenable_if::type
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type>
Run Code Online (Sandbox Code Playgroud)
失败,但使用通用模板
template <class T, class Enable = void>
class X;
Run Code Online (Sandbox Code Playgroud)
声明但未定义.
这很有用,因为您可以拥有更多专业化,例如:
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T>
using enable_if_floating_t = typename std::enable_if<std::is_floating_point<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
template <class T>
class X<T, enable_if_floating_t<T>> {
};
Run Code Online (Sandbox Code Playgroud)
希望你发现这至少有趣.
新年快乐!
编辑
我应该在哪里放入
<T, enable_if_integral_t<T>>函数定义?我只能用类模板完成这个...
对于函数,enable_if :: type可以是返回类型.例如,如果f返回int,您可以:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value, int>::type f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // error
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并与using:
#include <type_traits>
template <class T, class Return = void>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value, Return>::type;
template <class T>
enable_if_integral_t<T, int> f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // Error
return 0;
}
Run Code Online (Sandbox Code Playgroud)