不使用它的 constexpr 成员函数?
constexpr member functions that don't use this?
请考虑以下两个c++ 14程序:
程序1:struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
项目2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
这两个程序都是病态的吗?
为什么/为什么不?
两个程序都是格式良好的。c++ 14标准要求s.f()
是一个常量表达式,因为它被用来初始化constexpr
变量,事实上,它是一个核心常量表达式,因为没有理由不这样做。表达式可能不是核心常量表达式的原因在第5.19 p2节中列出。特别是,它指出表达式的求值必须做以下几件事中的一件,这些都没有在您的示例中完成。
constexpr
函数也会导致结果是非常量表达式。例如:constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
然而,这是错误格式的原因是因为非常量表达式的左值到右值转换,这是表达式的求值不允许做的事情之一。在调用s.f()
的情况下不会发生左值到右值的转换。
我似乎在标准中找不到一个令人信服的段落或例子来直接解决在非constexpr实例上调用constexpr成员函数的问题,但这里有一些可能会有所帮助(来自N4140草案):
[C++14: 7.1.5/5]
:
非模板、非默认
constexpr
函数或非模板、非默认、非继承constexpr
构造函数,如果不存在使函数或构造函数调用的实参值可能是核心常量表达式(5.19)的求值子表达式,则程序是病态的;没有诊断需要。
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
从这里我认为程序不是完全病态的,仅仅因为constexpr函数有一个可能的非constexpr路径。
[C++14: 5.19]
:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
这在某种程度上更接近于您的示例程序,这里constexpr构造函数可以根据参数的值引用非constexpr变量,但如果没有实际采用该路径,则不会出现错误。
所以我不认为你所呈现的程序应该是病态的,但是我不能提供令人信服的证据:)
这听起来像是一个测验问题,而且不是由学生提出的,而是教授在测试公众对stackoverflow的使用,但是让我们看看…
让我们从一个定义规则开始。很明显,两个版本都没有违反这一点,所以它们都通过了这一部分。
然后,到语法。两者都没有语法错误,如果您不介意潜在的语法和语义混合问题,它们都可以顺利编译。
首先是更简单的语义问题。这不是语法问题,但在两个版本中,f()
都是结构体的成员,而且该函数显然没有改变所属结构体,它返回一个常量。虽然该函数声明为constexpr,但没有声明为const,这意味着如果有某种理由将其作为运行时函数调用,如果在const s上进行调用,则会产生错误。这将影响两个版本。
现在,潜在的模棱两可的return g(S());
很明显,外面的g是一个函数调用,但是S可能不像写return g(S{});
那样清晰,如果用{}初始化S,将来struct S
用操作符()展开就不会有模棱两可了(结构体已经很像一个函子了)。现在调用的构造函数是自动生成的,并且在这个版本中没有操作符()会给编译器造成混淆,但现代c++ 14应该提供更清晰的替代方案,以避免类似于g(S())的"最令人烦恼的解析"。
所以,我不得不说,基于语义规则,它们都失败了(虽然不是那么严重)。
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 将公共但非静态的成员函数与ALGLIB集成
- 使用指向成员的指针将成员函数作为参数传递
- 将重载的成员函数传递给函数模板
- 我不小心调用了一个没有自己类对象的成员函数.但这是怎么回事呢
- 如何在C++中使用非静态成员函数作为回调函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 关联容器的下界复杂性:成员函数与非成员函数
- 在 C++ 中用派生类型重写成员函数
- 链表的泛型函数remove()与成员函数remove)
- 如何将lambda作为模板类的成员函数参数
- constexpr构造函数需要常量成员函数时出现问题
- 将自由函数绑定为类成员函数
- 区分非成员函数和头文件中的成员函数
- 如何从子成员函数修改父公共成员变量
- 保留对其他类的成员函数的引用
- 在运算符重载定义中使用成员函数(const错误)
- 内联如何影响模块接口中的成员函数
- 将成员函数指针作为参数传递给模板方法