c++11首选的限制允许的模板参数类型的方法
c++11 prefered way to limit allowed template argument types
让我们假设,我有一个模板函数foo(T)
,我只想接受T
的整数类型。所以main.cpp
可能看起来像这样:
int main()
{
int i = 1;
foo(i); // Should work fine
foo(&i); // Should not compile
}
现在,有两种选择可以实现这一点,我知道:
1) 使用static_assert
#include <type_traits>
template<typename T> void foo(T value) {
static_assert(std::is_integral<T>::value, "Not integral!");
// Logic goes here
}
此选项的优点是,我可以指定错误消息。缺点是,我的g++4.9.3确实会因为我的类型T
和// Logic
之间的差异而产生很多错误输出。
2) 在模板参数列表中使用std::enable_if
#include<type_traits>
template<typename T, class = typename std::enable_if<std::is_integral<T>::value>::type>
void foo(T value) {
// Logic goes here
}
缺点是产生的错误输出不能很好地描述问题,优点是它的数量要少得多。(将这两种方法结合起来可以减少错误输出的数量。)
问题
为了完整性,还有其他(好的)方法可以达到同样的结果吗?
哪个选项应该占用更少的编译时间?
据我所知,这两种方法在foo()
中允许的类型上是等效的。是吗?
我要问的是关于";在一个好的代码中应该如何做到这一点;以及什么时候使用什么方法的条件。
感谢您的意见。
为了说明实现目标的不同技术,我对示例进行了重大更改。我希望它仍然保持你问题的精神。
假设我有一个用于存储有理数的类型,并且我希望它可以从int
:隐式转换
struct Rational1
{
int i_;
Rational1(int i) : i_(i) {}
};
现在,它可以工作,但现在(由于隐式转换)它也可以从double
转换,我们不想要它:
Rational1 r = 2.5; // BUG (will be interpreted as 2.0)
我可以想出三种不同的方法来预防它(你已经提到了2):
struct Rational2
{
int i_;
template <typename T>
Rational2(T i) : i_(i)
{ static_assert(std::is_same<T, int>::value, "msg"); }
};
struct Rational3
{
int i_;
template <typename T, typename std::enable_if<std::is_same<T, int>::value, int>::type = 0>
Rational3(T i) : i_(i) {}
};
struct Rational4
{
int i_;
Rational4(int i) : i_(i) {}
template <typename T>
Rational4(T i) = delete;
};
测试它:
Rational2 r = 2.5; // compile-time error
Rational3 r = 2.5; // compile-time error
Rational4 r = 2.5; // compile-time error
但是,如果你用std::is_convertible
检查,结果是不同的:
static_assert(std::is_convertible<double, Rational2>::value = true, "");
static_assert(std::is_convertible<double, Rational3>::value = false, "");
static_assert(std::is_convertible<double, Rational4>::value = false, "");
这表明Rational2
可以转换为double
:构造函数模板被选择并编译。该标准要求内部的static_assert
触发编译时错误(并带有您选择的消息),但它不会停止编译:这就是您看到更多消息的原因。
Rational3
和enable_if
的情况使模板对于int
以外的类型不可见,因此,它不能进一步检查函数内部的错误,但编译器可以查找其他构造函数,并可能选择另一个。
案例Rational4
明确指出,如果我们试图从除int
之外的任何其他内容进行转换,则应将其视为硬错误:这对std::is_convertible
也是可见的。
(但是,在您的示例中不能使用此技术,因为您将约束到元函数(is_integral
)而不是具体类型。)
另一个选项是让编译器只专门处理您需要的类型。
举例说明:通常,您需要为所有模板类和函数的编译器提供实现:
template<typename T>
class MyClass {
public:
MyClass(T arg) { d_val = arg; }
privateL
T d_val;
};
因此,以下内容将起作用,因为编译器将能够实现所有这些内容:
MyClass<double> classDouble;
MyCalss<int> classInt;
但是,如果你不提供实现,那么编译器就无法实现它们,所以你需要:
MyClass.h
template<typename T>
class MyClass {
public:
MyClass(T arg);
privateL
T d_val;
};
MyClass.cpp
template<typename T>
MyClass::MyClass() { d_val = arg; }
/* The explicitly make the one for int */
MyClass<int> tmpInt;
使用:
MyCalss<int> classInt; // Will compile since the compiler already generated the code when it compiled the cpp file
MyClass<double> classDouble; // Wont compile since the compiler does not know how to produce the code.
这种方法的问题在于,您需要在源文件(cpp)中指定所需的每种类型。
我将这种方法用于16种不同类型的模板化类。添加新类型有点问题,但它为我节省了很多代码来避免重做。这是一个非常具体的问题,有着非常具体的解决方案。
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型