In Thinking in C++ by Bruce eckel, there is an example given regarding friend functions as
// Declaration (incomplete type specification):
struct X;
struct Y {
void f(X*);
};
struct X { // Definition
private:
int i;
public:
friend void Y::f(X*); // Struct member friend
};
void Y::f(X* x) {
x->i = 47;
}
Now he explained this:
Notice that Y::f(X*) takes the address of an X
object. This is critical because the compiler always knows how to
pass an address, which is of a fixed size regardless of the object
being passed, even if it doesn’t have full information about the size
of the type. If you try to pass the whole object, however, the
compiler must see the entire structure definition of X, to know the
size and how to pass it, before it allows you to declare a function
such as Y::g(X).
But when I tried
void f(X);
as declaration in struct Y, it shows no error.
Please explain why?
The parameter types for function declarations may be incomplete.
For data member declarations and all definitions however, the type has to be complete:
struct A;
struct B {
void f(A); // declaration, fine
void g(A) {} // error
A a; // error
};
It should be fine to pass the incomplete object X by value, though during the function implementation the complete type X must be available irrespective of whether the object of type X is actually used in function or not.
A function declaration is all fine with incomplete object as argument or return type, except covariant return type (for member function) where such return type must be complete type.
The problems is when you change the declaration in struct X.
So, inside struct X you tell the compiler the struct has a function which receives something of type X, but wait a minute, type X is not fully define at this point!
You can't use an incomplete type as a parameter for function f but you can use the address of an incomplete type, thus X*.
On a site note, it will be very important for you to increase your acceptance rate so you get answers for your future questions and important for my ego if i got the right answer :)
Related
I wonder why the language allows declaring a pointer to a member function/data although that member type doesn't exist and as we know that at compile-time, the static type must be know and the type must be full type before usage of that type apart from using an incomplete type in so restricted situations.
Here is my example:
struct Foo{
int bar(bool, bool){std::cout << "Foo::bar\n"; return x_;}
int x_ = 10;
void do_it()const{cout << "do_it()\n";}
void do_it(int, bool)const{cout << "do_it(int, bool)\n";};
};
int main(){
int (Foo::* pMemBar)(bool, bool) = &Foo::bar; // ok
(Foo{}.*pMemBar)(0, 0); // ok
int Foo::*pMemX = &Foo::x_;
std::cout << Foo{}.*pMemX << '\n';
std::string (Foo::* pMemFn)(char)const; // why this is allowed?
std::string Foo::* pMemDt = nullptr; // why allowed
}
As you can see everything is OK until the declaration of pointer pMemFn, that pointer is a pointer to a member function of class Foo that is const and takes a single argument aschar and returns an std::string. But there is no such version in the class Foo and as we know the compiler do knows all the members of a class so why it allows that? I know this pointer is not yet de-referenced thus there is no object of that class in such declaration and the compiler complains only when de-referencing it using a object of that type class Foo but why the compiler allows such declaration?
I think it would be more suitable for the compiler to refuse such declarations at the first place as it do for the static type of a pointer to a base class. What do you think? And is there a philosophy behind allowing that? Thank you!
Well first of all, you are allowed to declare pointer-to-member of class X before class X is defined (i.e., only a forward declaration of X has been seen). So in those cases, the compiler isn't yet able to determine that there exists no member of X with the appropriate type.
Beyond that, there are reasons why such pointer-to-member types are needed despite the fact that there is no actual member of that type. First, static_cast can be done both up and down the class hierarchy: in other words, let's say we have
struct Bar : Foo {
std::string s;
};
Now, an implicit conversion always exists from T Foo::* to T Bar::* since this is a safe conversion: if a T Foo::* value denotes a particular member of Foo, then any Bar object has such a member as well. When you use static_cast, it is possible to go the other way around: converting from T Bar::* to T Foo::*. You might wonder why one would ever want to do this. Well, it's not terribly common, but the idea is that just like how Foo* can point to any object derived from Foo (thus providing a common type with which to refer to such objects, enabling runtime polymorphism), T Foo::* also can point to any member of type T of any class derived from Foo. But you have to exercise caution, since using such a pointer requires knowledge that the member it denotes actually exists in the object pointed to.
Anyway, while (again) it's not terribly common, the point is that the compiler can't reject a std::string Foo::* because, for all it knows, there might be a class derived from Foo (possibly in another translation unit) that actually contains a std::string member.
Also, it's possible to reinterpret_cast between pointers to members of unrelated classes and unrelated member types. The rule is that T X::* can always be cast to U Y::* as long as T and U are both object types or both function types, except that you can't cast away constness this way (but you can do so with an additional const_cast). So I can do this for example:
struct Unused {};
using ObjType = int;
using FuncType = void();
And I can use ObjType Unused::* to hold pointers to any object type of any class; and I can use FuncType Unused::* to hold pointers to any function type of any class. But such pointers must be cast back to their original types before they can be used.
In C++11, is there a clean way to disable implicit conversion between typedefs, or do you have to do something nasty like wrap your int in a class and define and delete various operators?
typedef int Foo;
typedef int Bar;
Foo foo(1);
Bar bar(2);
bar = foo; // Implicit conversion!
HelloWorld explains why what you have cannot work. You'll need what's typically called a "strong" typedef to do what you want. An example implementation is BOOST_STRONG_TYPEDEF:
#include <boost/serialization/strong_typedef.hpp>
BOOST_STRONG_TYPEDEF(int, a)
void f(int x); // (1) function to handle simple integers
void f(a x); // (2) special function to handle integers of type a
int main(){
int x = 1;
a y;
y = x; // other operations permitted as a is converted as necessary
f(x); // chooses (1)
f(y); // chooses (2)
}
If we had done typedef int a;, then the code would be ambiguous.
The C++ standard says:
7.1.3 The typedef specifier
A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in
the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does
not introduce a new type the way a class declaration (9.1) or enum declaration does
But e.g. class or struct introduce new types. In the following example uniqueUnused does actually nothing but is used to create a different type Value<int, 1> != Value<int, 2>. So maybe this is something you are looking for. Keep in mind there is no guarantee the compiler gets rid of the outer structure! The only guarantee this code gives you it's the same size as int
template<typename T, int uniqueUnused>
struct Value
{
Value() : _val({}) {}
Value(T val) : _val(val) { }
T _val;
operator T&() { return _val; }
// evaluate if you with or without refs for assignments
operator T() { return _val; }
};
using Foo = Value<int, 1>;
using Bar = Value<int, 2>;
static_assert(sizeof(Foo) == sizeof(int), "int must be of same size");
static_assert(sizeof(Bar) == sizeof(int), "int must be of same size");
If you want to create a new type based on a class you can simply go with this example (this doesn't work with scalar types since you can't inherit from ints):
class Foo : public Bar // introduces a new type called Foo
{
using Bar::Bar;
};
I wanted to do something similar to keep different indexes separated not only logically, but also enforced by the compiler. The solution I came up with is basically to just define structs with one element. In some ways it's more painful to use, but it works very well with my situation since I often don't need to deal with the actual value of the index for a lot of my code, just passing it around.
typedef struct{uint16_t i;} ExpressionIndex;
typedef struct{uint16_t i;} StatementIndex;
Now, trying to do
ExpressionIndex foo() {
StatementIndex i;
return i;
}
yields the error error: incompatible types when returning type ‘StatementIndex’ but ‘ExpressionIndex’ was expected
Converting between types is a bit painful, but that was the intent of my change.
ExpressionIndex exp = (ExpressionIndex){stmt.i};
It's not strict type-checking, but illegal conversions can made visible by using original, or Apps Hungarian Notation (H. N.). If you think H. N. means name-type-as-prefix, you're wrong (it's System H. N., and it's, hm, unnecessary naming overhead).
Using the (Apps) H. N., the variable prefix marks not the type (e.g. int), but the purpose, e.g. counter, length, seconds etc. So, when you add a counter to a variable contains elapsed time, you write cntSomethingCounter + secElapsedSinceLastSomething, and you can see that it smells. Compiler does not alerts, but it pokes your eyes.
Read more: http://www.joelonsoftware.com/articles/Wrong.html
I was looking at the emulated version of nullptr and saw this converting operator (a member of nullptr_t):
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
This syntax for pointer to member function confuses me. I usually expect to see such a type as something like
R (C::*)(I1, I2, ...)
With the template above, there's no input arguments. I can't figure out how the type deduction works in this case. I'm having trouble forming a specific question, other than, how does this work? If I have code like this:
typedef int (MyClass::*MyTypedef)(float);
MyTypedef m = nullptr;
I'm guessing T deduces to int, and C deduces to MyClass. What "happens" to float?
That is a pointer to member, not necessarily a pointer to member function. The difference is that it can generate a pointer to member function or a pointer to non-function member.
Now in the particular use case, the destination is a pointer to member, the compiler is seeing an expression in which it needs a int (MyClass::*)(float), and on the other hand it has a nullptr. It tries to find a conversion and it finds the operator T C::*(), which is a valid conversion if C is deduced to be MyClass and T is deduced to be int (float) [function taking a float and returning an int].
I also find this particular corner of the language a bit confusing (having typedefs, or deduced types for functions), for example this is legal if weird:
typedef void int_f(int);
struct X {
int_f m;
};
void X::m(int x) { std::cout << x << '\n'; }
The same thing is going on in the conversion operator that you are concerned with.
Can anyone explain why the following code does not compile (on g++ (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-49))?
struct X {
public:
enum State { A, B, C };
X(State s) {}
};
int main()
{
X(X::A);
}
The message I get is:
jjj.cpp: In function 'int main()':
jjj.cpp:10: 'X X::A' is not a static member of 'struct X'
jjj.cpp:10: no matching function for call to 'X::X()'
jjj.cpp:1: candidates are: X::X(const X&)
jjj.cpp:5: X::X(X::State)`
Is this bad code or a compiler bug?
Problem solved by Neil+Konrad. See the comments to Neil's answer below.
You've forgot the variable name in your definition:
int main()
{
X my_x(X::A);
}
Your code confuses the compiler because syntactically it can't distinguish this from a function declaration (returning X and passing X::A as an argument). When in doubt, the C++ compiler always disambiguates in favour of a declaration.
The solution is to introduce redundant parentheses around the X since the compiler forbids parentheses around types (as opposed to constructo calls etc.):
(X(X::A));
X(X::A);
is being seen a s a function declaration. If you really want this code, use:
(X)(X::A);
Just to make it crystal clear what happens. Look at this example
int main() {
float a = 0;
{
int(a); // no-op?
a = 1;
}
cout << a;
}
What will it output? Well, it will output 0. The int(a) of above can be parsed in two different ways:
Cast to int and discard the result
Declare a variable called a. But ignore the parentheses around the identifier.
The compiler, when such a situation appears where a function-style cast is used in a statement and it looks like a declaration too, will always take it as a declaration. When it can't syntactically be a declaration (the compiler will look at the whole line to determine that), it will be taken to be an expression. Thus we are assigning to the inner a above, leaving the outer a at zero.
Now, your case is exactly that. You are trying (accidentally) to declare an identifier called A within a class called X:
X (X::A); // parsed as X X::A;
The compiler then goes on to moan about a not declared default constructor, because the static, as it assumes it to be, is default constructed. But even if you had a default constructor for X, it of course is still wrong because neither A is a static member of X, nor a static of X can be defined/declared at block scope.
You can make it not look like a declaration by doing several things. First, you can paren the whole expression, which makes it not look like a declaration anymore. Or just paren the type that is cast to. Both of these disambiguations have been mentioned in other answers:
(X(X::A)); (X)(X::A)
There is a similar, but distinct ambiguity when you try to actually declare an object. Look at this example:
int main() {
float a = 0;
int b(int(a)); // object or function?
}
Because int(a) can be both the declaration of a parameter called a and the explicit conversion (cast) of the float-variable to an int, the compiler decides again that that is a declaration. Thus, we happen to declare a function called b, which takes an integer argument and returns an integer. There are several possibilities how to disambiguate that, based on the disambiguation of above:
int b((int(a))); int b((int)a);
You should declare an object as
X x(X::A);
Bug in your code.
Either of these two lines work for me:
X obj(X::A);
X obj2 = X(X::A);
As Neil Butterworth points out, X(X::A) is being treated as a function declaration. If you really want an anonymous object, (X)(X::A) will construct an X object and immediately delete it.
You could, of course, just do something like this:
int main()
{
// code
{
X temp(X::A);
}
// more code
}
This would be more readable and basically have the same effect.
For example:
struct test
{};
void thing(test())
{}
int main()
{
thing(test());
}
This code would give me error; however, the next example won't give me error:
void thing(int())
{}
int main()
{
thing(int());
}
My main question is, why the first example isn't possible and the second one is? Ultimately, both test and int are types, so I can't think why declaring an anonymous object of test in the thing function argument list isn't possible whereas declaring an anonymous object of type int in the thing function argument list is.
It is possible; it's just that you're doing it wrong.
Here is a declaration of a function taking an unnamed parameter of type test:
void thing(test);
Here is a declaration of a function taking an unnamed parameter of type pointer-to-function-returning-test:
void thing(test());
You want the former, not the latter.
That your second code example works is actually a magical oddity, stemming from the fact that int() is 0 is a valid null pointer constant, which may be used to initialise a function pointer; the example breaks as soon as you swap int() for some other integer, or if you run the code in a completely compliant C++14 compiler (because C++14 made it so that 0 but not int() is a valid null pointer constant).