我想知道对于长循环我们是否可以在C++ 11中利用constexpr的尾递归?
通常,constexpr必须没有副作用.但是,我刚刚发现可以在抛出异常的构造函数中使用副作用.该技术可用于模拟constexpr函数的assert(),如下面的程序所示.
#include <iostream>
#include <cstdlib>
#include <stdexcept>
struct constexpr_precond_violated : std::logic_error
{
constexpr_precond_violated(const char* msg) :
std::logic_error(msg)
{
std::cerr << msg << '\n';
abort(); // to get a core dump
}
};
#define TO_STRING_IMPL(x) #x
#define TO_STRING(x) TO_STRING_IMPL(x)
#define CONSTEXPR_PRECOND(cond, value) \
((!(cond)) ? throw constexpr_precond_violated( \
"assertion: <" #cond "> failed (file: " \
__FILE__ ", line: " TO_STRING(__LINE__) ")") \
: (value))
constexpr int divide(int x, int y)
{
return CONSTEXPR_PRECOND(y != 0, x / y);
}
int …Run Code Online (Sandbox Code Playgroud) 这是本主题的某种后续行动,涉及其中的一小部分.与前一个主题一样,让我们考虑一下我们的编译器有和的constexpr函数.现在,让我们直截了当地说.std::initializer_liststd::array
这有效:
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr int a0 = a[0];
constexpr int a1 = a[1];
constexpr int a2 = a[2];
constexpr std::initializer_list<int> b = { a0, a1, a2 };
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这不是:
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr std::initializer_list<int> b = { a[0], a[1], a[2] …Run Code Online (Sandbox Code Playgroud) 当我尝试编译以下代码时,我得到一个链接器错误:Undefined symbols for architecture x86_64: "Foo()", referenced from: _main in main.o使用LLVM 4.2.
仅在标记函数时才会出现此问题constexpr.当标记功能时,程序会正确编译和链接const.为什么声明该函数constexpr会导致链接器错误?
(我意识到以这种方式编写函数不会带来编译时计算的好处;此时我很好奇为什么函数无法链接.)
main.cpp中
#include <iostream>
#include "test.hpp"
int main()
{
int bar = Foo();
std::cout << bar << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
test.hpp
constexpr int Foo();
Run Code Online (Sandbox Code Playgroud)
TEST.CPP
#include "test.hpp"
constexpr int Foo()
{
return 42;
}
Run Code Online (Sandbox Code Playgroud) 我的理解是constexpr类类型的全局变量几乎无法使用,因为
必须在每个TU中定义这样的对象,因为constexpr不允许对象的前向声明.
默认链接static会导致在内联函数中命名对象(ODR使用与否)以违反ODR,因为相应的inline定义具有不同的含义.
extern constexpr如果对象是ODR使用的,则每个TU的一个定义的声明将违反ODR规则,这在对其进行引用时发生.
this参数,即使它已被成员函数使用.extern constexpr即使没有使用ODR,也会声明对象,GCC和Clang都会抱怨ODR违规(多个定义).这都是正确的吗?有没有办法让一个constexpr全局类类型没有包装在一个inline函数中?
我知道c ++ 11允许使用constexpr为编译时而不是运行时指定表达式.
我知道这也可以用于类的构造函数.但是,此构造函数必须初始化类的所有成员,以便用作constexpr(在编译时),并且它调用的任何函数也应标记为constexpr.
但是什么时候会将他的构造函数设置为constexpr,除了轻微优化之外还有其他任何好处吗?
这是否意味着,如果我可以,我应该总是将我的构造函数设置为constexpr?
可以X::f()在下面的代码中使用虚函数
struct X
{
constexpr virtual int f() const
{
return 0;
}
};
Run Code Online (Sandbox Code Playgroud)
是constexpr吗?
据我所知,一个非常常见的情况是这样的
template<int i> class Class
{
public:
static constexpr int I = i;
static constexpr int J = constexprFunction(i);
// further Class implementation
};
Run Code Online (Sandbox Code Playgroud)
几乎同样普遍的我看到错误(事实上我的大部分问题是因为我忘记了,并且不知道,正确的问题是什么)如果成员使用的话会忘记附加的定义:
template<int i> constexpr int Class<i>::I;
template<int i> constexpr int Class<i>::J;
Run Code Online (Sandbox Code Playgroud)
现在我读了cppreference:Definitions和ODR以及cppreference:静态成员,声明这是C++ 17不推荐使用的.这对我来说似乎很棒,因为它避免了很多错误.但是还有其他问题出现了:
1)这改变了其他原因,而不是使附加定义无用吗?(另见本问题的最后一段)
2)在cppreference的最后一个例子中:静态成员似乎也适用于const static成员 - 但规则只声明了constexpr成员.是否适用于const static会员?
3)所有的例子,我发现用一个简单的定义一样Class::I-做这一切还保持了形势Class:J与constexpr功能?
简要说明在C++ 17和C++ 17之前最佳实践是什么.总而言之,这对我来说似乎是一个非常棘手的变化,因为它会产生很多代码,这些代码之前需要"格式化不需要诊断"才能获得良好的代码(据我所知......).因此,会产生代码,对于较早的(前17版)编译器仍然是"不需要的非诊断" - 但只要不需要使用odr,这些就不会抱怨.
编辑:更正了Aaron McDaid建议的文字.
我想我非常理解如何将关键字constexpr用于简单的变量类型,但是当涉及到指向值的指针时,我感到很困惑.
我想声明一个constexpr C字符串文字,它的行为就像
#define my_str "hello"
Run Code Online (Sandbox Code Playgroud)
这意味着编译器将C字符串文字插入到我输入此符号的每个位置,并且我将能够在编译时使用sizeof获取其长度.
是吗 constexpr char * const my_str = "hello";
要么 const char * constexpr my_str = "hello";
要么 constexpr char my_str [] = "hello";
还是还有别的什么?
请考虑以下代码:
template<typename T>
struct S { static constexpr int bar = T::foo; };
struct U: S<U> { static constexpr int foo = 42; };
int main() { }
Run Code Online (Sandbox Code Playgroud)
GCC v6.1编译它,clang 3.8拒绝它并带有错误:
2:错误:'U'
结构中没有名为'foo'的成员S {static constexpr int bar = T :: foo; };
哪个编译器是对的?
可能是因为我们尝试在其中使用它时U 不是一个完整的类型S吗?
在这种情况下,它应该被认为是GCC的一个错误,但我想知道我是否正好在bug跟踪器上搜索/打开一个问题...
编辑
与此同时,我向GCC 打开了一个错误.
等待它接受答案.