Can virtual functions like X::f() in the following code
struct X
{
constexpr virtual int f() const
{
return 0;
}
};
be constexpr?
This answer is no longer correct as of C++20.
No. From [dcl.constexpr]/3 (7.1.5, "The constexpr specifier"):
The definition of a constexpr function shall satisfy the following requirements:
— it shall not be virtual
Up through C++17, virtual functions could not be declared constexpr. The general reason being that, in constexpr code, everything happen can at compile time. So there really isn't much point to having a function which takes a reference to a base class and calls virtual functions on it; you may as well make it a template function and pass the real type, since you know the real type.
Of course, this thinking doesn't really work as constexpr code becomes more complex, or if you want to share interfaces between compile-time and runtime code. In both cases, losing track of the original type is easy to do. It would also allow std::error_code to be more constexpr-friendly.
Also, the fact that C++20 will allow us to do (limited) dynamic allocation of objects means that it is very easy to lose track of the original type. You can now create a vector<Base*> in constexpr code, insert some Derived class instances into it, and pass that to a constexpr function to operate on.
So C++20 allows virtual functions to be declared constexpr.
Can virtual functions be constexpr?
Yes. Only since C++20, virtual functions can be constexpr.
Related
I've been thinking why constexr and virtual are mutually exclusive, and someone added:
... constexpr is all about execution at compile time; if we're executing the function at compile time, we obviously know the type of data upon which it's acting at compile time as well, so late binding clearly isn't relevant.
However, it's possible that the dynamic type is not identical to the static type even in compile-time, and there might be cases where the dynamic type is needed:
class A {
public:
/* virtual */ constexpr int foo() {
return 1;
}
};
class B : public A {
public:
constexpr int foo() {
return 2;
}
};
constexpr int foo(A &a) {
// The static type is fixed here.
// What if we want to call B::foo() ?
return a.foo();
}
int main() {
B b;
constexpr int c = foo(b);
return 0;
}
That is, my question is
what's the (possible) rationale behind the standard prohibiting the combination of the two?
This restriction exists since constexpr was introduced in C++11:
10.1.5 The constexpr specifier [dcl.constexpr]
3 The definition of a constexpr function shall satisfy the following requirements:
(3.1) - it shall not be virtual;
But you're asking about rationale for this restriction, and not about the restriction itself.
The fact is, it may just be an oversight. Since in a constant expression the dynamic type of the object is required to be known, this restriction is unnecessary and artificial: This is what Peter Dimov and Vassil Vassilev assert in P1064R0, where they propose to remove it.
In fact, the wording for it no longer exists in the current draft.
Because virtual calls are resolved via vtables/RTTI (RunTime Type Information) located in the memory layout of your object at runtime, the "true type" behind the "used object handle" is not known at compile time.
In your example:
constexpr int foo(A &a) {
// The static type is fixed here.
// What if we want to call B::foo() ?
return a.foo();
}
If the foo member function is virtual there is no way for that foo function to be executed at compile time. And there is no point in marking a function as constexpr if it can never be executed at compile time. Hence there is no point in ever marking something both constexpr and virtual.
Now technically, depending on the code complexity, multiple cases might be resolvable by adding a completely new system (other than RTTI) to resolve virtual calls that would be compile time compatible (e.g some form of compiler meta data when everything is constexpr that remembers what type of instance you put in a pointer/reference) but that simply is not a thing right now.
If you want a runtime indirection (which is what virtual does) it doesn't make much sense to also want that to be executed at compile time.
P.S: sorry for the "compile time" and "runtime" spam.
Indeed, it is possible for compilers to know at compile time what is the dynamic type of a constant expression. This is a possibility, that is some time used by optimizer to devirtualize calls at compile time.
But the c++ language evolution take also in consideration how difficult it is to implement it in compilers. If such a consideration were not taken, then the c++ standard will be too far from the c++ coded so that it would be unusefull.
Maybe, it has been evaluated that keeping trac of the static type of every reference during constant expression evaluation would be too expensive to implement. This is where reality hurts!
Look at this snippet:
struct A {
void fn();
};
struct B: A {
};
void f() {
auto x = &B::fn;
}
Here, x gets a type of void (A::*)(), despite the fact that I've written &B::fn.
If I added fn into B, then type of x would be void (B::*)().
So, type of &B::fn changes whether B has a fn, or not.
What is the rationale behind this behavior? I find it surprising.
Why does this matter? Suppose this: programmer X creates A and B, like in my example. Programmer Y uses &B::fn, and uses the class-part of its type for something (like a parameter to a template, whatever). Then programmer X realizes, that he needs some extra functionality in fn, so he overrides it. Now, programmer Y's code can be broken, as type of &B::fn have changed.
This was the subject of CWG issue 203 and EWG issue 89. Initially, the rationale was to allow as much code as possible to be valid:
Notes from 04/00 meeting:
The rationale for the current treatment is to permit the widest possible use to be made of a given address-of-member expression. Since a pointer-to-base-member can be implicitly converted to a pointer-to-derived-member, making the type of the expression a pointer-to-base-member allows the result to initialize or be assigned to either a pointer-to-base-member or a pointer-to-derived-member. Accepting this proposal would allow only the latter use.
Later, after the problems caused by it had become more obvious, it was too late to fix:
Additional note, April, 2015:
EWG has determined that the utility of such a change is outweighed by the fact that it would break code. See EWG issue 89.
I think that the main idea comes from that usual "B is A" class inheritance definition. You can rephrase it for member functions like "functions of A are functions of B", however statement with flipped A and B position is correct only for some items, that is "only some functions of B are functions of A". So B::fn fits into this category of functions of B that are functions of A. By writing function fn in class B we at the same time move B::fn out of this category into category of functions of B that are not functions of A.
This allows one to check whether class overrides some method of base class:
const bool fn_is_overriden{::std::is_same<decltype(&A::fn), decltype(&B::fn)>::value};
In most ways, a member function is like a free function with an implicit object argument. So this code:
struct A {
void fn();
};
is very similar to:
struct A {};
void A_fn(A* this_);
For example, overload resolution between member functions and free functions is defined in this way, so that all of the functions can be ranked on the same footing.
When you inherit a member (variable or function), you inherit that function as it is. Inheritance doesn't define new functions or variables. All it does is create names in the derived class that refer to the existing members of the base class.
You've still got A_fn(A* this), just that it is accessible from B's scope. The access rules for inheritance apply to the inherited names only, they don't change any properties of the member.
template<typename T> struct Derived: T
{
/*static*/ int foo(int x) { return T::foo(x) + 1; }
};
If T::foo(int x) is static then Derived<T>::foo(int x) should also be static. Otherwise Derived<T>::foo(int x) should be non-static.
Is there a way to let the compiler take care of this?
No, you cannot propagate staticness in the sense you ask. Incidentally, you could also ask the same thing about const:
int foo(int x) { return bar(x) + 1; } // Infer that foo is const because bar is
C++ specifiers are meant to convey intent about the interface, on which users can rely even if the implementation changes:
static int foo(x); // This method does not require an object
int foo(x) const; // This method will not modify the object
In case - through templates, for example - the implementation may vary, your interface must reflect the lowest common denominator. For const, for example, methods need to be non-const. For static, which is your question, you cannot declare static.
Note that this is not a huge imposition. Even if a method is static, you can still call it using with object semantics. So, in your case, you'll have to just use object semantics. In particular, regarding your clarification in the comments
If allocator is static then container doesn't need to hold it's pointer. So decorators must preserve staticness.
note that decorators can also not preserve staticness, because containers can hold pointers in any case, and call them via object notation, regardless of their constness.
Use below construct:
static_assert(std::is_same<decltype(&Derived::foo), decltype(&T::foo)>::value or
(std::is_member_function_pointer<decltype(&Derived::foo)>::value and
std::is_member_function_pointer<decltype(&T::foo)>::value),
"Derived::foo & T::foo are not having same static-ness");
I have tested quickly with my g++ and it works fine.
What it does
Takes address of both the methods and if they are comparable then
they must be static. This means that the methods signatures are
expected to be same (as implied in your example).
If (1) fails then check if both the foo are function pointers of
their respective classes types. Here no strictness of type, but you can impose with some more meta programming. Leaving up to you.
If both of above fails, then the compiler gives an error. This static_assert can be put within class Derived.
Notes: (1) If Derived::foo is static & T::foo is not then anyways, it gives error. (2) or & and are official keywords of C++. If certain compilers like MSVC doesn't support then use || & && respectively.
Why is operator () of stateless functor not allowed to be static? Stateless lambda objects are convertible to pointers to free functions having the same signature as their operator ().
Stephan T. Lavavej on p. 6 points out that conversion to a function pointer is just an operator FunctionPointer() (cite). But I can't obtain a corresponding pointer to operator () as to non-member function. For functor struct F { void operator () () {} } it seems to be impossible to convert &F::operator () to instance of type using P = void (*)();.
Code:
struct L
{
static
void operator () () const {}
operator auto () const
{
return &L::operator ();
}
};
The error is
overloaded 'operator()' cannot be a static member function
but operator () is not overloaded.
Per standard 13.5/6,
An operator function shall either be a non-static member function or be a non-member function and have
at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an
enumeration.
Additionally, in 13.5.4 it is stated that
operator()
shall be a non-static member function with an arbitrary number of parameters. It can have
default arguments. It implements the function call syntax
postfix-expression
(
expression-list
opt
)
where the
postfix-expression
evaluates to a class object and the possibly empty
expression-list
matches
the parameter list of an
operator()
member function of the class. Thus, a call
x(arg1,...)
is interpreted
as
x.operator()(arg1, ...)
for a class object
x
of type
T
I would think that there's no technical reason to forbid this (but not being familiar with the de-facto cross-vendor C++ ABI (Itanium ABI), I can't promise anything).
There's however an evolutional issue about this at https://cplusplus.github.io/EWG/ewg-active.html#88 . It even has the [tiny] mark on it, making it a somewhat "trivial" feature under consideration.
I can't see any technical reason to forbid a static auto operator()( ... ). But it's a special case, so it would complicate the standard to add support for it. And such complication is not necessary, because it's very easy to emulate:
struct L
{
static void func() {}
void operator()() const { func(); }
operator auto () const
{
return &L::func;
}
};
See Johannes' answer for some possibly useful extra info.
Like the others, I don't see a fundamental reason why it is not possible to have a static operator(), for stateless functors or in general.
(EDIT 2020: Just found this proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1169r0.html)
(UPDATE 2021: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r1.html)
In some cases might conflict, according to other rules, with member/static overloading which is not allowed in C++ (again, not sure why).
struct A{
void f();
static int f(); // compile error
}
So even if it were allowed to have a static operator(), should this be permitted?
struct A{
void operator()();
static int operator()(); // should be a compiler error???
}
Anyway, there is only one true reason to have a static operator() that it is not purely a syntactic reason and it is that objects should be able to call static functions as if they were member functions.
struct A{
static int f():
}
...
A a;
a.f(); // calls A::f() !!!
Specifically, the user of the class A doesn't need to know if a function is implemented as static or as a member.
It can later be upgraded to a member function from a generic point of view.
Leaving that important application to generic programming aside, there is a workaround the leads to a similar syntax that I saw in https://quuxplusone.github.io/blog/2018/03/19/customization-points-for-functions/, and that is to have a static member function called _, a name that doesn't imply any meaning.
struct A{
static int _();
}
...
A::_(); // instead of the more desirable (?) A::() or A::operator()
a._(); // this invokes the static functon _
Instead of the more desirable A::() or A::operator(), (well are they desirable at all? I don't know; something like A() would be really interesting but doens't even follow the syntax of a static function and can be confused with a constructor).
(As I said, the only feature I still miss, regarding this limitation you point out, is that a() cannot automatically delegate to a static version of the operator(), e.g. A::operator().)
In summary, your code could look like this:
struct L{
static void _() {}
auto operator()() const{
return L::_();
}
};
L ell;
ell(); // calls L::_() really.
Not sure what are willing to achieve still.
One can "CRTP" the idea https://godbolt.org/z/74vxsTYxd
#include<utility> // forward
template<class Self>
struct stateless_functor_facade{
template<class... Args>
constexpr decltype(auto) operator()(Args&&... args) const{
return Self::operator_paren(std::forward<Args>(args)...);
}
};
struct square : stateless_functor_facade<square>{
static auto operator_paren(double x){return x*x;}
};
int main(){
square s;
s(5);
}
Starting with C++23 both operator() (P1169) and operator[] (P2589) can be static. There was never really a good reason for the restriction, so it's finally being lifted.
You can also define a Lambda as static by [] static { ... }. If you have a Lambda without captures, you always should define it as static.
Unfortunately, this behavior cannot become the implicit standard without breaking API and ABI compatibility, so the explicit definition is necessary. Backwards compatibility is, as so often, both a blessing and a curse.
i i'm not going to pretend to be an expert at all. however, i was looking into static lamdas or static operator() and theirs an ongoing proposal about it. i was satisfied with what i could understand.
it appears the ideas proposed is grappling with how to design it to not break other code.etc. however it appears their working on a solution. so. mabyeeee one day!
static operator()
Document #: P1169R2
Date: 2021-08-14
Project: Programming Language C++
3 Proposal
The proposal is to just allow the ability to make the call operator a
static member function, instead of requiring it to be a non-static
member function. We have many years of experience with member-less
function objects being useful. Let’s remove the unnecessary object
parameter overhead. There does not seem to be any value provided by
this restriction.
There are other operators that are currently required to be
implemented as non-static member functions - all the unary operators,
assignment, subscripting, conversion functions, and class member
access. We do not believe that being able to declare any of these as
static will have as much value, so we are not pursuing those at this
time. We’re not aware of any use-case for making any of these other
operators static, while the use-case of having stateless function
objects is extremely common.
find full Document here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r2.html
It is now possible to have static operator (), after c++23 - go to https://en.cppreference.com/w/cpp/feature_test#Language_features and look for __cpp_static_call_operator. GCC 13 will supports it - https://gcc.gnu.org/projects/cxx-status.html.
Here is an example on Compiler Explorer with gcc trunk.
struct A {
constexpr static int operator() (int x) noexcept { return x - 1; }
constexpr static int operator[] (int x) noexcept { return x - 1; }
};
int main() {
return [](int x) constexpr static noexcept { return x - 1; }( A()( A()[3] ));
}
A simple, a little bit dirty workaround until the relevant committee considers this trivial feature:
Glob operators are syntactically similar to the constructors.
Thus, you can't write a
static MyClass::operator()(...);
It was made simply impossible, because a committee decided so on unclear reasons. I would be so happy if I could talk with one of their members, to ask, what was in their mind as they decided so. Unfortunately, he probably wouldn't understand my question, because he never programmed c++. He only worked on its docs.
Now they need some decades of
debates
consultations
meetings
and considerations
to implement a trivial feature. I suspect the feature may be made available around in c++3x, and on emulated machines it may be even tried out.
Until then, you can try to write:
MyClass::MyClass(...);
In both cases you can call MyClass(...);.
Of course it is useful mainly if MyClass is a singleton. And, I would say it is a hack. Furthermore, it allocates a sizeof(MyClass) on the stack which may be bad in the performance / efficiency side.
Furthermore, this will be in essence a constructor, and constructors can't return anything. But you can avoid this by storing the result in the instance, and then casting it by a cast operator to anything you wish to.
We know that C++ doesn't allow templated virtual function in a class. Anyone understands why such restriction?
Short answer: Virtual functions are about not knowing who called whom until at run-time, when a function is picked from an already compiled set of candidate functions. Function templates, OTOH, are about creating an arbitrary number of different functions (using types which might not even have been known when the callee was written) at compile-time from the callers' sides. That just doesn't match.
Somewhat longer answer: Virtual functions are implemented using an additional indirection (the Programmer's General All-Purpose Cure), usually implemented as a table of function pointers (the so-called virtual function table, often abbreviated "vtable"). If you're calling a virtual function, the run-time system will pick the right function from the table. If there were virtual function templates, the run-time system would have to find the address of an already compiled template instance with the exact template parameters. Since the class' designer cannot provide an arbitrary number of function template instances created from an unlimited set of possible arguments, this cannot work.
How would you construct the vtable? Theoretically you could have an infinite number of versions of your templated member and the compiler wouldn't know what they might be when it creates the vtable.
The other answers have already mentionned that virtual functions are usually handled in C++ by having in the object a pointer (the vptr) to a table. This table (vtable) contains pointer to the functions to use for the virtual members as well as some other things.
The other part of the explanation is that templates are handled in C++ by code expansion. This allow explicit specialization.
Now, some languages mandate (Eiffel -- I think it is also the case of Java and C#, but my knowledge of them is not good enough to be authoritative) or allow (Ada) an shared handling of genericity, don't have explicit specialization, but would allow virtual template function, putting template in libraries and could reduce the code size.
You can get the effect of shared genericity by using a technique called type erasure. This is doing manually what compilers for shared genericity language are doing (well, at least some of them, depending on the language, other implementation techniques could be possible). Here is a (silly) example:
#include <string.h>
#include <iostream>
#ifdef NOT_CPP
class C
{
public:
virtual template<typename T> int getAnInt(T const& v) {
return getint(v);
}
};
#else
class IntGetterBase
{
public:
virtual int getTheInt() const = 0;
};
template<typename T>
class IntGetter: public IntGetterBase
{
public:
IntGetter(T const& value) : myValue(value) {}
virtual int getTheInt() const
{
return getint(myValue);
}
private:
T const& myValue;
};
template<typename T>
IntGetter<T> makeIntGetter(T const& value)
{
return IntGetter<T>(value);
}
class C
{
public:
virtual int getAnInt(IntGetterBase const& v)
{
return v.getTheInt();
}
};
#endif
int getint(double d)
{
return static_cast<int>(d);
}
int getint(char const* s)
{
return strlen(s);
}
int main()
{
C c;
std::cout << c.getAnInt(makeIntGetter(3.141)) + c.getAnInt(makeIntGetter("foo")) << '\n';
return 0;
}
I think it's so that compilers can generate vtable offsets as constants (whereas references to non-virtual functions are fixups).
When you compile a call to a template function, the compiler usually just puts a note in the binary, effectively telling the linker "please replace this note with a pointer to the correct function". The static linker does something similar, and eventually the loader fills in the value once the code has been loaded into memory and its address is known. This is called a fixup, because the loader "fixes up" the code by filling in the numbers it needs. Note that to generate the fixup, the compiler doesn't need to know what other functions exist in the class, it just needs to know the munged name of the function it wants.
However with virtual functions, the compiler usually emits code saying "get the vtable pointer out of the object, add 24 to it, load a function address, and call it". In order to know that the particular virtual function you want is at offset 24, the compiler needs to know about all the virtual functions in the class, and what order they're going to appear in the vtable. As things stand, the compiler does know this, because all the virtual functions are listed right there in the class definition. But in order to generate a virtual call where there are templated virtual functions, the compiler would need to know at the point of the call, what instantiations there are of the function template. It can't possibly know this, because different compilation units might instantiate different versions of a function template. So it couldn't work out what offset to use in the vtable.
Now, I suspect that a compiler could support virtual function templates by emitting, instead of a constant vtable offset, an integer fixup. That is, a note saying "please fill in the vtable offset of the virtual function with this munged name". Then the static linker might fill in the actual value once it knows what instantiations are available (at the point where it removes duplicate template instantiations in different compilation units). But that would impose a serious burden of work on the linker to figure out vtable layouts, which currently the compiler does by itself. Templates were deliberately specified to make things easier on implementers, in the hope that they might actually appear in the wild some time before C++0x...
So, I speculate that some reasoning along these lines led the standards committee to conclude that virtual function templates, even if implementable at all, were too difficult to implement and therefore could not be included in the standard.
Note that there's a fair bit of speculation in the above even before I try to read the minds of the committee: I am not the writer of a C++ implementation, and nor do I play one on television.