Take note of the following C++ code:
#include <iostream>
using std::cout;
int foo (const int);
int main ()
{
cout << foo(3);
}
int foo (int a)
{
a++;
return a;
}
Notice that the prototype of foo() takes a const int and that the definition takes an int. This compile without any errors...
Why are there no compilation errors?
Because it doesn't matter to the caller of the foo function whether foo modifies its copy of the variable or not.
Specifically in the C++03 standard, the following 2 snippets explain exactly why:
C++03 Section: 13.2-1
Two function declarations of the same name refer to the same function if they are in the same scope and
have equivalent parameter declarations (13.1).
C++03 Section: 13.1-3
Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.
Top-level const (i.e., that applies to the value that's passed, not something to which it points or refers) affects only the implementation, not the interface, of a function. The compiler ignores it from the interface viewpoint (i.e., the calling side) and enforces it only on the implementation (i.e., code in the body of the function).
As others have explained, the Standard says it's ok, and that the compiler can afford to be lenient about enforcing this because it doesn't affect the caller, but nobody's answered why the compiler should choose to be lenient. It's not particularly lenient in general, and a programmer who's just been looking at the interface then dives into the implementation may have it in the back of their mind that a parameter is const when it's not or vice versa - not a good thing.
This leniency allows implementation changes without modifying headers, which using traditional make tools triggers recompilation of client code. This can be a serious issue in enterprise scale development, where a non-substantive change in a low-level header (e.g. logging) can force rebuilding of virtually all objects between it and the applications... wasting thousands of hours of CPU time and delaying everyone and everything waiting on the builds.
So, it's ugly, but a practical concession.
I've also answered another similar question which looks at why overloading of f(const T) and f(T) isn't allowed - may be of interest to anyone reading this - Top-level const doesn't influence a function signature
int foo (const int a)
{
a++;
return a;
}
That'll throw an error during compiling.
Related
GNU C and C++ offer the const and pure function attributes.
From the gnu online docs (emphasis mine):
In GNU C and C++, you can use function attributes to specify certain function properties that may help the compiler optimize calls or check code more carefully for correctness. For example, you can use attributes to specify that a function never returns (noreturn), returns a value depending only on the values of its arguments (const), or has printf-style arguments (format).
Where the const attribute seems to be a subset to pure, also taken from the gnu docs:
The const attribute imposes greater restrictions on a function’s
definition than the similar pure attribute. Declaring the same
function with both the const and the pure attribute is diagnosed.
With C++ 11, the constexpr specifier was added.
When applied to functions, is there a difference between the const attribute and the constexpr specifier? Does GCC apply different optimizations?
A similar sounding question is Difference between `constexpr` and `const` . But I think this is not a duplicate. My question is specifically about the function attribute const, which seems to have overlapping functionality with constexpr.
When applied to functions, is there a difference between the const attribute and the constexpr specifier?
There are differences.
Firstly, C does not have constexpr, so you cannot take advantage of it in that language.
Calls to constexpr function can be constant expressions. As such, their result can be used for example as the size of an array. The GNU attributes cannot be used to achieve the same (ignoring GCC VLA language extension).
Constexpr functions are good for taking advantage of pre-calculation at compile time. The GNU attributes are still useful for allowing the compiler take advantage of runtime constness. For example, let's say there is a function that cannot be constexpr - perhaps because it calls a non-constexpr function. But we may know that every call to the function produces same output with no side-effects. Const attribute allows the compiler to not repeat redundant calls.
Another difference is that constexpr functions must be defined inline. Non-constexpr functions don't need to be defined inline.
There is very little overlap between the them. Here is a function that is gnu::const (and therefore gnu::pure) but cannot be constexpr:
[[gnu::const]] int f() {
struct s {
int m;
};
union {
s a;
s b;
};
a.m = 1;
return b.m;
}
You cannot read from the common initial subsequence of a union in a constant expression, so this function never produces a constant expression.
The following is a function that is constexpr but cannot be gnu::pure (and therefore cannot be gnu::const):
constexpr void f(int & x) {
x = 1;
}
This writes to memory, which gnu::pure functions cannot do.
constexpr just means that it can be called at compile time. This has overlap with gnu::const and gnu::pure in that you cannot write to global variables or access volatile memory (all IO operations access volatile memory from C++'s point of view), but they each have other restrictions that make them distinct.
In C++11, constexpr was much more limited. You could not write to any reference parameters, so I think C++11 constexpr was a strict subset of gnu::pure. The following is an example of a C++11 constexpr function that cannot be marked gnu::const, however:
[[gnu::pure]] constexpr int f(int const & x) {
return x;
}
Another difference is that a __attribute__ ((const)) function must return the same output value for the same argument (if I am not wrong; it's the first time I see this attribute ;). That does not need to hold for constexpr functions.
An academic example:
constexpr int rand(int n)
{
std::string_view sv(__TIME__);
return sv.back() % n;
}
std::array<int, rand(10) + 1> a; // exemplary usage
Though rand is constexpr, it may produce different output for the same input argument.
I am almost sure this has been asked before. Unfortunately, my C++ has become so rusty that I don't even know what to search for.
Is there an easy-to-remember rule of thumb that would tell me which qualifiers (inline, virtual, override, const, mutable, etc.) must appear (a) only in the declaration, (b) only in the definition, (c) both in the declaration and definition when I define a member function out-of-line?
Example:
struct Geometry {
…
virtual Geometry* clone() const = 0;
};
struct Point2 : public Geometry {
…
virtual Point2* clone() const override { … }
};
If I wanted to define Point2::clone out-of-line, trial and error leads me to believe that this would be the correct code:
struct Point2 : public Geometry {
…
virtual Point2* clone() const override;
};
Point2* Point2::clone() const { … }
The virtual and override qualifiers may appear only in the declaration.
const must appear in both the declaration and definition.
I would not want to rely on trial and error forever. But I want to be explicit about qualifiers (i.e. repeat them even if e.g. they are implied by a base class.) Is there a general rule which qualifier has to go exactly where, or are the rules different for each of them?
General rule is that when removing a qualifier produces a different function overload, that qualifier must appear in both places. All other qualifiers stay in the declaration.
The three qualifiers that must appear in both places are const and the two kinds of reference qualifiers, which appear in the C++11 standard:
void foo() const;
void foo() &;
void foo() &&;
All other qualifiers stay in the declaration.
The best way to know which modifiers (or "specifiers") have to be used in which instance is to understand what each one of them means, and what they do.
const is a part of the method's "signature". A signature consists of what the function returns, the types of its parameters, and whether it is a constant method (the const part). That cannot be changed, and must remain as is.
Everything else, virtual, and override in your list, is related to the method's declaration. It's not a part of the method's signature, and can only appear in the method's declaration.
The only rule of thumb, if there is one, is that anything that's a part of the method's signature must be unchanged, when the method is not defined inline. And if it's not, it must be a part of the declaration, only (but, as with every rule of thumb, there's always an exception, the inline keyword).
Note that default parameter values are not considered to be a part of the method's signature. Default parameter values must be specified in the declaration only. But, if the method is defined inline, the default parameter values wind up as part of the method's definition!
I will take another approach: My rule of thumb: put them in both places and then do what the compiler says. It implements the standard rules and it will make you follow them.
The problem with any rule of thumb is that you can't be sure it's ok for a particular example, so why not check from the start by default.
If you seldom use C++, there is no point in learning some rules that you can't 100% rely on them anyway. If you (start to) use C++ often, then after several times the compiler tells you what to do, you will get the gist yourself.
Because this post is not in the same tone with the others, I will go the rogue way all the way and give you this extremely unused with a dark corner case example: constexpr in an explicit instantiation
template <class T>
constexpr auto foo(T)
{
}
template constexpr auto foo(int);
// ^
// 6 : error: explicit instantiation shall not use 'constexpr' specifier
It's not what you asked about, but is goes to show that you can apply this strategy to a broader set of similar problems, where you would otherwise need other rules of thumbs
struct int_holder {
int value;
int triple() {return value*3;}
};
int main(int argc, const char * argv[])
{
std::string abc{"abc"};
int_holder one{1};
auto f1 = mem_fn(&std::string::clear);
auto f2 = mem_fn(&int_holder::triple);
f1(abc);
f2(one);
}
i test such code in Xcode and the compiler issues such error
it seems mem_fn is fine with member functions of user-defined class but not with member functions of standard string, what's the different, and why?
thanks for your reading, help me plz!
I can reproduce this with Clang 3.1-3.3 as well as 3.6. Looks like bug 16478.
Simplest fix is to just use a lambda or equivalent. Other completely non-portable workarounds include either disabling extern templates with
#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...)
#endif
before you include any headers (in essence applying r189610); and doing an explicit instantiation of either the member function (template void std::string::clear();) or the entire class.
That said, you should not take the address of a member function of a standard library class. [member.functions]/p2:
An implementation may declare additional non-virtual member function
signatures within a class:
by adding arguments with default values to a member function signature;187
by replacing a member function signature with default values by two or more member function signatures with equivalent behavior; and
by adding a member function signature for a member function name.
187) Hence, the address of a member function of a class in
the C++ standard library has an unspecified type.
As for the standard, you can't take a pointer to any standard nonstatic member because the library implementation is allowed to add hidden overloads, defaulted function template parameters, SFINAE in the return type, etc.
In other words, & std::string::clear is simply not a supported operation.
In terms of Clang, it looks like an issue with hidden symbol visibility. Each shared object (linker output) file gets its own copy of certain functions to avoid the appearance that third-party shared libraries implement the standard library. With different copies floating around, the equality operator over PTMFs would not work: If you retain the value & std::string::clear from an inline function, it might not compare equal to itself later. However, it's probably a bug, since std::string should be completely implemented by the libc++.so shared library. Only other specializations of std::basic_string could really justify this behavior.
A good fix would be to use a lambda instead. []( std::string & o ) { o.clear(); } is a superior alternative to mem_fn. It avoids the indirect call and its sizeof is smaller. Really, you shouldn't use mem_fn unless absolutely necessary.
While reading Wikipedia's page on decltype, I was curious about the statement,
Its [decltype's] primary intended use is in generic
programming, where it is often
difficult, or even impossible, to name
types that depend on template
parameters.
While I can understand the difficulty part of that statement, what is an example where there is a need to name a type that cannot be named under C++03?
EDIT: My point is that since everything in C++ has a declaration of types. Why would there ever be a case where it is impossible to name a type? Furthermore, aren't trait classes designed to yield type informations? Could trait classes be an alternative to decltype?
The wikipedia page you link has a perfect example:
int& foo(int& i);
float foo(float& f);
template <class T> auto transparent_forwarder(T& t) −> decltype(foo(t)) {
return foo(t);
}
Note that foo(int&) returns int& (a reference type) while foo(float&) returns float (a nonreference type). Without decltype, it's impossible within the template to specify a type which represents "the return type of the function foo which takes an argument t of type T".
In this example, it's not a particular concrete type which is impossible to express -- either int& or float are individually expressible -- but a higher level generic class of types.
EDIT: and to answer your comment to another answer, this example is inexpressible in C++03. You cannot have a function template which will wrap any function T1 foo(T2) and match both argument and return type of the wrapped function.
There are types in C++0x (and in C++03, but less often) that cannot be named explicitly, such as the type decltype(f) after the declaration auto f = [](int x) -> int {return x;};. You would need to typedef that decltype result to something to get a name at all. Traits classes can be used for determining return types, but they are messy, and the user needs to duplicate all of their function overloads with traits class overloads; that is difficult to do correctly for cases such as functions applying (through implicit conversion of pointers) to all subclasses of a given base class.
As you pointed out, the type if it exist is known by the compiler, otherwise it wouldn't exist. However, it is not always readily or even accessible to the programmer in C++03.
N1607 mention the following in its conclusion:
In C++2003, it is not possible to
express the return type of a function
template in all cases. Furthermore,
expressions involving calls to
function templates commonly have very
complicated types, which are
practically impossible to write by
hand
The question is how do we access this type as a programmer. This is not always a trivial process, often impracticable. It is increasingly complex when you have an expression for which you desire to know the result type. You would have to break it into pieces in order to figure the result types. It is not possible to simplify this process using templates (not without evaluating the expression anyhow). Breaking the expression will be error-prone, tedious and a nightmare to maintain. Think of this code:
x.g()[b.a(e)]->f();
With C++98/TR1, it is often infeasible to name types that depend on template parameters. Traits offers us so much information, but eventually decltype is a much cleaner solution to many problems. A lot of the information available to you when meta programming is only available because libraries, such as boost or loki, use several tricks hidden in the dark corners of the C++98 language.
Of course this is irrelevant to your question but I believe that it is worthy to mention that C++98 compilers already have mechanics to know these types. This is exactly what sizeof offers, except that it returns you a size. decltype reuse some of this functionality and solves these problems with greater elegance.
As for a different (academic) example:
struct Foo
{
struct
{
int x;
} bar;
};
template<typename T>
void
f(const T& t)
{
// C++03, How can I name the type of T::bar ?
// C++0x
// decltype(t.bar) cpy;
// Do stuff with our local cpy
}
int
main()
{
f(Foo());
}
Take note of the following C++ code:
#include <iostream>
using std::cout;
int foo (const int);
int main ()
{
cout << foo(3);
}
int foo (int a)
{
a++;
return a;
}
Notice that the prototype of foo() takes a const int and that the definition takes an int. This compile without any errors...
Why are there no compilation errors?
Because it doesn't matter to the caller of the foo function whether foo modifies its copy of the variable or not.
Specifically in the C++03 standard, the following 2 snippets explain exactly why:
C++03 Section: 13.2-1
Two function declarations of the same name refer to the same function if they are in the same scope and
have equivalent parameter declarations (13.1).
C++03 Section: 13.1-3
Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.
Top-level const (i.e., that applies to the value that's passed, not something to which it points or refers) affects only the implementation, not the interface, of a function. The compiler ignores it from the interface viewpoint (i.e., the calling side) and enforces it only on the implementation (i.e., code in the body of the function).
As others have explained, the Standard says it's ok, and that the compiler can afford to be lenient about enforcing this because it doesn't affect the caller, but nobody's answered why the compiler should choose to be lenient. It's not particularly lenient in general, and a programmer who's just been looking at the interface then dives into the implementation may have it in the back of their mind that a parameter is const when it's not or vice versa - not a good thing.
This leniency allows implementation changes without modifying headers, which using traditional make tools triggers recompilation of client code. This can be a serious issue in enterprise scale development, where a non-substantive change in a low-level header (e.g. logging) can force rebuilding of virtually all objects between it and the applications... wasting thousands of hours of CPU time and delaying everyone and everything waiting on the builds.
So, it's ugly, but a practical concession.
I've also answered another similar question which looks at why overloading of f(const T) and f(T) isn't allowed - may be of interest to anyone reading this - Top-level const doesn't influence a function signature
int foo (const int a)
{
a++;
return a;
}
That'll throw an error during compiling.