我们采取一个简单的SFINAE templating example
#include <iostream>
template <typename T>
struct has_typedef_foobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::foobar*);
template <typename>
static no& test(...);
// If the "sizeof" of the result of calling test<T>(0) would be equal to sizeof(yes),
// the first overload worked and T has a nested type named foobar.
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
struct foo {
typedef float foobar;
};
int main() {
std::cout << std::boolalpha;
std::cout << has_typedef_foobar<int>::value << std::endl;
std::cout << has_typedef_foobar<foo>::value << std::endl;
}
bool
值尚未声明为constexpr
,但它仍然在编译时获得其值。那么constexpr
的使用是什么?为什么简单的静态变量在编译时得到它们的值?如何判断哪些静态变量在编译时获取它们的值?
另外,我如何判断在编译时将评估哪些值,哪些不会?使用constexpr
会保证编译时评估吗?如果不是,我怎么知道会发生什么(编译时或运行时)?
答案 0 :(得分:9)
constexpr
给你的关键之一是之前无法实现的关键是能够在需要编译时常量的上下文中调用函数。最简单的例子是数组大小:
#include <iostream>
using namespace std;
constexpr auto square(int x) { return x * x; }
int main()
{
int arr[square(2)] = { 0, 1, 2, 3 };
cout << arr[square(1)] << endl;
}
如果函数constexpr
没有square()
,则无法在arr
的定义中调用它,因为数组大小需要是编译时常量。
虽然您可以在square
之前编写一个可以提供编译时constexpr
的模板,但是您无法使用具有非编译时常量参数的模板,因此最终会出现代码重复用于编译时和非编译时版本。对于大多数程序员来说,模板的语法比简单的函数定义更复杂,更不熟悉。
事实上,constexpr
几乎不能保证编译器何时会在编译时实际选择评估某些内容。在需要值为编译时常量(例如数组大小)的上下文中,它当然是。在其他情况下,它主要取决于编译器 - 在访问square()
时调用arr[square(1)]
,例如编译器可以在运行时自由评估,尽管在实践中我希望大多数编译器在编译时对此进行评估时间,至少在优化的构建中。
答案 1 :(得分:7)
这不是一个很好的问题,因为constexpr
是一个巨大的功能,它有许多&#34;点&#34;它。
主要的是,你可以使用(更多)普通的C ++语法进行编译时计算而不是模板元编程语言。
例如,如果您曾尝试使用模板在编译时进行字符串操作,则可以将此体验与此处描述的一些技术进行对比:Conveniently Declaring Compile-Time Strings in C++
constexpr基本上是一种新的编译时元编程语言,它与模板语言并行存在,而constexpr构造函数之类的东西允许你在编译时实例化结构,这对于仅模板是不可能的。
另一个重点&#34;点&#34;这就是你不必为编译时与运行时编写不同的代码。标记为constexpr 的代码可以在编译时运行,也可以在运行时轻松运行。模板代码...必须完全在编译时运行,并且通常看起来与等效的运行时代码非常不同。所以在某些情况下使用constexpr代码更干。