我有这个代码,用于在控制器的LCD显示器上构建图形界面;使用AVR和PIC32为2种不同的体系结构编译代码:
FishinoTftGuiLabel *l1;
FishinoTftGui
.Page(F("Page1"))
.Label(50, 140, 0, 24, LabelAlign::Left, F("Slider value:"))
.getElement(l1)
--
.Label(l1->x() + l1->w() + 10, 140, 0, 24, LabelAlign::Left, F("pippo"))
;
每个成员函数返回相同的对象(或相关的对象);例如,Label()函数返回FishinoTftGuiLabel引用,该引用可用于链接其他调用。
getElement(T *&)只是获取指向当前对象的指针的一种方法,它可以在以下调用中使用而不会破坏链并且必须为每个对象使用中间变量; - 运算符返回包含的Page对象。
我的问题是'l1'指针,应该在第一个Label创建时由getElement设置,在整个东西终止之后设置,但只是在AVR平台上;在PIC32上评估是可以的。
因此,在PIC32上,订单如下:
1)评估第一个Label语句并创建标签
2)执行getElement(l1),存储第一个标签的引用
3)评估第二个Label语句; l1-> x()正确使用Label1引用
在AVR平台上,会发生这种情况:
1)首先评估所有Label()调用的参数,因此l1-> x()崩溃,因为调用未初始化对象的成员
2)接下来评估Label()函数
我的问题:这是一个编译器错误,还是在这种情况下无法保证链接调用之间的评估顺序?有没有办法强制正确的评估顺序,而不必在多个语句中打破整个东西?
答案 0 :(得分:2)
正如评论中所提到的,直到C ++ 17,函数参数中未测序的子表达式的评估顺序是未指定的,因此它不是编译器错误:两个排序都是允许的(如果这些表达式导致导致未定义的行为不止一个读/写相同的标量变量,例如f(i++,i++)
)。
从C ++ 17开始,后缀表达式(如函数调用)从左到右进行评估;函数参数的评估顺序仍未指定,但不能交错。因此,自C ++ 17以来,您的代码将始终提供所需的结果。
作为一种解决方法,您可以让Label和朋友也接受lambdas作为参数,以支持懒惰(因此有序)评估,例如:
template<typename T>
auto constref_or_evaluate(T const& t) -> T const&
{ return t; }
template<typename T>
auto constref_or_evaluate(T&& t) -> decltype(std::forward<T>(t)())
{ return std::forward<T>(t)(); }
// the type of FishinoTftGui
struct FishinoTftGuiType
{
// chainable members ...
template<typename... T>
auto Label(T&&... t) -> FishinoTftGuiType&
{
LabelImpl( constref_or_evaluate(std::forward<T>(t))... );
return *this;
}
private:
// the original chainable member implementations ...
void LabelImpl(int,int); //whatever
};
// to be used as
FishinoTftGui
.Label(1,2)
.Label([]{return 3;},4);
这里第二个Label()中的lambda将在完全评估第一个Label()之后被调用。
这样做的另一个好处是可以更好地控制何时计算惰性表达式(比如,每当视图调整大小时标签都可以更新惰性参数等等)。因此,在&gt; = C ++ 17代码中也可能值得考虑。
据我所知,这只是C ++ 11;无论如何,如果你还想传递l / rvalue参考参数,你需要编写一个forward_or_evaluate()
函数;这在C ++ 11中是完全可行的,但它实现起来有点困难。