Basically this is a follow up of this question about most vexing parse. I can understand that this is due to the ambiguity between the function declaration and variable definition.
But in Comeau online, I just tired the following.
class T{
public:
T(int i){
}
int fun1(){
return 1;
}
};
int main()
{
T myT(10); // I thought it'd be a function declaration that takes an int and returns a type T
myT.fun1(); // and a compiler error out here.
}
But it compiles fine and there were no errors. I looked into the standard docs but couldn't come to a reasoning.
So, what am I missing here?
Because 10 is not a type. :)
This would be a Most Vexing Parse:
T myT(T());
// T() gets interpreted as function pointer argument to a function returning T();
// This is equivalent to:
T myT(T (*fn)());
Another variety of the Most Vexing Parse is this one:
unsigned char c = 42;
T myT(int(c));
// int(c) gets interpreted as an int argument called c.
// This is equivalent to:
T myT(int c);
The 10 cannot be a parameter type name, so this must be a variable declaration.
The compiler must choose a function declaration when it can do that, but in many cases like this it cannot and there is no ambiguity.
It's not a vexing parse because you used an integer literal rather than, say:
T myT(T());
As in this complete example:
#include <iostream>
struct T { int f() { return 1; } };
int main(int argc, char** argv) {
T t(T());
std::cout << t.f() << '\n';
return 0;
}
Which is ambiguous because it could mean:
myT is a T initialised with a default-constructed T; or
myT is a function returning a T and taking one argument of type T(), which denotes a zero-argument function whose return type is also T.
The latter interpretation is the default one, which is why a compiler error results from attempting to use the newly declared function as though it were the object you expected it to be.
See the Wikipedia article about it.
Related
std::is_function is specialized for types which have signature similar to:
int(int) &
see here:std::is_function
But this is neither a pointer to a member method, which signature could be:
int(T::*)(int) &
Nor it can be a reference to a function:
int (&)(int)
So what is this strange signature?
It's a function type which only exists in the type system. It cannot ever be created.
But this is neither a pointer to a member method, which signature could be:
int(T::*)(int) &
It's this, without the pointer. The type system allows you to describe that as a type.
#include <type_traits>
struct T { };
using A = int(int) &;
using B = A T::*;
using C = int(T::*)(int) &;
static_assert(std::is_same_v<B, C>);
#T.C. mentions PR0172R0, which discusses how the presence of these types causes issues for library writers, and suggests several options which might reduce those issues. One of the options is getting rid of them entirely, others reduce their impact. Depending on how this goes, this answer may or may not be correct for future versions of C++.
On the documentation page you link to, you'll see this comment:
// specialization for function types that have ref-qualifiers
above the list the examples you reference come from.
Those are functions with ref-qualifiers, which you can read more about here.
In short, they are similar to const qualified functions. Here's an example:
struct foo
{
void bar() & { std::cout << "this is an lvalue instance of foo" << "\n"; }
void bar() && { std::cout << "this is an rvalue instance of foo" << "\n"; }
};
int main(int argc, char* argv[])
{
foo f{};
f.bar(); // prints "this is an lvalue instance of foo"
std::move(f).bar(); // prints "this is an rvalue instance of foo"
return 0;
}
I can't think of a great use case for this feature, but it is possible to use.
Since the beginning of times (referring to the first C++ standard) you could declare such "strange" function types as, for example
typedef int F() const;
Despite the fact that the above declaration does not immediately involve any classes, the trailing const in this case can only serve as const-qualification of a non-static class member function. This restricts the usage of the above typedef-name to class member declarations. For example, one could use it as follows
struct S {
F foo; // Declares an `int S::foo() const` member function
};
int S::foo() const { // Defines it
return 42;
}
F S::*p = &S::foo; // Declares 'p' as `int (S::*)() const` pointer
Note that, however obscure, this is a "classic" C++ feature that's been in the language for a long time.
What you have in your example is effectively the same thing, but with C++11 ref-qualifier in place of const qualifier.
Is this an example of a vexing parse in C++?
#include <pthread.h>
#include <iostream>
class ScopeLock
{
public:
ScopeLock(pthread_mutex_t& m)
: mrMutex(m)
{
pthread_mutex_lock(&mrMutex);
}
~ScopeLock()
{
pthread_mutex_unlock(&mrMutex);
}
protected:
pthread_mutex_t& mrMutex;
};
class Foo
{
public:
Foo()
{
pthread_mutex_init(&m_, NULL);
}
~Foo()
{
pthread_mutex_destroy(&m_);
}
void Func()
{
ScopeLock(m_); // Is this a vexing parse?
std::cout << __FUNCTION__ << std::endl;
}
protected:
pthread_mutex_t m_;
};
int main(int argc, char* argv[])
{
Foo foo;
foo.Func();
return 0;
}
Output:
>g++ main.cpp
main.cpp: In member function \u2018void Foo::Func():
main.cpp:37:17: error: no matching function for call to 'ScopeLock::ScopeLock()'
ScopeLock(m_);
^
main.cpp:37:17: note: candidates are:
main.cpp:7:3: note: ScopeLock::ScopeLock(pthread_mutex_t&)
ScopeLock(pthread_mutex_t& m)
^
main.cpp:7:3: note: candidate expects 1 argument, 0 provided
main.cpp:4:7: note: ScopeLock::ScopeLock(const ScopeLock&)
class ScopeLock
^
main.cpp:4:7: note: candidate expects 1 argument, 0 provided
I think the compiler fails because it is trying to create a ScopeLock object (named m_) with no ctor arguments, and correctly identifies that the only ScopeLock ctor takes one pthread_mutex_t& as argument -- is that correct?
Why is this a vexing parse, though (if it is)? Why is line 37 not interpreted as creation of an anonymous ScopeLock object with ctor argument m_?
If the above is an example of a vexing parse, why is the below not a vexing parse?
#include <iostream>
#include <string>
class Foo
{
public:
Foo()
{
pStr = "Foo";
}
~Foo()
{
}
void Func()
{
const std::string& rStr = std::string(pStr);
std::cout << rStr << std::endl;
}
protected:
const char* pStr;
};
int main(int argc, char* argv[])
{
Foo foo;
foo.Func();
return 0;
}
Compilation and output:
>g++ main.cpp
>./a.out
Foo
>
The second code block seems very analogous to the first. Why, then does the compiler not treat line 18 as creation of a std::string named pStr with no ctor arguments? The cout of rStr resulting in "Foo" actually shows that an anonymous std::string was created with const char* argument.
I'd be grateful if anyone could shed light here. Thank you.
Update
I just noticed that in the first code block changing this:
ScopeLock(m_); // Is this a vexing parse?
to this:
const ScopeLock& rSl = ScopeLock(m_); // Is this a vexing parse?
results in compilation passing. So something about turning the anonymous object to an rvalue fixes the vexing parse problem? I'm unclear.
On the flip side, in the second code block, changing this:
const std::string& rStr = std::string(pStr);
to this:
std::string(pStr);
compiles just fine. However the resulting cout of pStr is empty. I think this confirms that the compiler is actually creating a std::string named pStr using the default constructor. So actually that is analogous to what it was trying to do in the first code block.
I'd still be grateful if someone could confirm if what I've surmised is correct.
The "canonical" meaning of the "most vexing parse" refers to the ambiguity between object declaration and function declaration.
What you have in your case is a different ambiguity: an ambiguity between object declaration and functional-style cast (more formally: functional notation of explicit type conversion, see 5.2.3). This latter ambiguity is resolved in favor of object declaration. Hence the error. Your code is seen by the compiler as a simple
ScopeLock m_;
which makes it to complain about missing default constructor.
6.8 Ambiguity resolution [stmt.ambig]
1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
Whether you want to call it another flavor of "most vexing parse" is up to you.
There are many different ways to make the compiler to interpret it as an expression instead of declaration. You can also do it as
0, ScopeLock(m_);
or as
(ScopeLock(m_));
Why is line 37 not interpreted as creation of an anonymous ScopeLock object with ctor argument m_?
It's because the Standard has a rule that if code has structure that can be interpreted as either a declaration or a function call, treatment as the declaration is chosen.
It doesn't worry about whether treatment as a declaration causes an error later.
If you think about it, having a fallback to an entirely different interpretation would cause some very nasty action-at-a-distance results during code maintenance. Imagine if you wrote this code and it was accepted as a function-style-cast, and later someone added a default constructor...
Try to replace
ScopeLock(m_); to ScopeLock s(m_);. The object needed its name.
-
Maybe compiler was seeing ScopeLock(m_) as ScopeLock m_. I compiled with clang, and it showed similar error message.
Following line compiles without any complaint :
void Func()
{
ScopeLock(m)(m_);
std::cout << __FUNCTION__ << std::endl;
}
Why is following not allowed in C++
#include <iostream>
class Sample {
public:
void Method(char x);
void Method(char const x);
};
void Sample::Method(char x) {
char y = x;
}
void Sample::Method(char const x) {
char y = x;
}
int main() {
Sample s;
return 0;
}
Why is following not allowed in C++?
The reason is the very same that the compiler gives you as an compilation error:
Because they are ambiguous!
Why are these methods ambiguous?
Short answer: Because the C++ Standard says so.
What is the rationale behind these overloaded methods being ambiguous?
The compiler does not know whether the caller wants to treat the value of the passed argument as an const or not, there is no way for the compiler to determine that with the information at hand.
Note the emphasis on pass by value here, the argument is being passed by value, and hence the ambiguity. If the argument was passed by reference then the compiler knows for sure how the caller wants to treat the argument because then the actual object itself is being passed, and hence compiler can make a selection of the proper overload.
The following example gives a clearer idea to the explanation above.
Online Sample:
class Sample
{
public:
void Method(char &x){}
void Method(char const x){}
void Method(char const &x){}
};
int main()
{
Sample s;
return 0;
}
It doesn't really answer why, but it is determined by the standard, §1.3.10
The information about a function that participates in overload resolution (13.3): the types of its parameters
and, if the function is a class member, the cv- qualifiers (if any) on the function itself and the class in which the member function is declared.
This just means the cv qualifiers of the arguments are ignored in the overload resolution.
A similar (but not equivalent) example with references works:
class Sample {
public:
void Method(char& x) {}
void Method(const char& x) {}
};
because here the types are different, the first case being a reference to char, the second a reference to const char (as opposed to a const reference to char).
When it comes to function parameters, char and char const are the same data type.
This is still ambiguous. When it's called with a character argument, one version will copy the argument and say "OK, you can change the copy". The other will copy the argument and say "OK, you cannot change the copy." How is the compiler supposed to know whether it can or can't change a copy of something? It could do either just fine.
because it's ambiguous
when you're passing like this
s.Method('x');
what version should you think be called?
The standard says those two declarations are equivalent (13.1.3):
Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called.
typedef const int cInt;
int f(int);
int f(const int); // redeclaration of f(int)
int f(int) { /* ... */ } // definiton of f(int)
int f(cInt) { /* ... */ } // error: redefiniton of f(int)
http://duramecho.com/ComputerInformation/WhyHowCppConst.html
Because const denotes that variable as having a set value, that cannot be changed after declaration. It is not a different data type.
Recently I've bumped into a weird behavior when having a class with a template constructor that accepts only one argument. This class under certain circumstances fails to initialize at all.
#include <iostream>
struct Foo
{
template<typename T>
Foo(const T& x)
{
std::cout << "--" << x << std::endl;
}
};
int main(int, char**)
{
Foo f(std::string());
std::cout << "++" << f << std::endl;
return 0;
}
What I have is a Foo structure with the constructor you see. In main I create a new Foo instance with an empty std::string. The example compiles and the output is:
++1
WTF??? As you may have noticed there are three problems with the this. First of all the constructor was never called, secondly the f was printed as "1", thirdly there is no overloaded operator<< that prints the Foo using the cout and despite that the compiler never complained. This means that the f is not a type of Foo.
I change the example slightly:
int main(int, char**)
{
Foo f(std::string(""));
std::cout << "++" << f << std::endl;
return 0;
}
...and the compiler throws an error that there is no overloaded operator<< and that is normal.
int main(int, char**)
{
Foo f(std::string("Hello"));
return 0;
}
Now the output is:
--Hello
and that's normal again.
The problem is when having passing to Foo::Foo a an empty object whether it is std::string() or float() or any_type(). I've tested the example in GCC 4.4 and 4.6 and I find this behavior very unexpected.
What is your opinion? Is this a GCC bug or am I missing something here?
Foo f(std::string());
It does not create an instance of type Foo, as you seem to think.
Instead, this declares a function of name f, whose return type is Foo, and parameter type is a function type (which takes no argument and returns std::string).
It is same as:
Foo f(std::string (*)()); //same as yours
The only difference this and your function declaration is that in your case the parameter type is function type (which eventually decays into function pointer type anyway), and in my case, the parameter type is function pointer type (which doesn't need to decay into pointer type).
As for why it prints ++1 is because it invokes operator<< which takes void* as argument, and this function prints the address of the function.
Now if you want to declare an object, then you need to use extra parens as:
Foo f((std::string())); //notice the extra parens!
This declares an object, and calls the constructor : http://ideone.com/Px815
See also this topic:
Most vexing parse: why doesn't A a(()); work?
You're experiencing the infamous C++ Most vexing parse. In your first example
Foo f(std::string());
is parsed as a function declaration, so no constructor is called.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Error with address of parenthesized member function
In this recent question the OP ran into a strange provision of the C++ language that makes it illegal to take the address of a member function if that member function name is parenthesized. For example, this code is illegal:
struct X {
void foo();
};
int main() {
void (X::* ptr)();
ptr = &(X::foo); // Illegal; must be &X::foo
}
I looked this up and found that it's due to §5.3.1/3 of the C++ ISO spec, which reads
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses [...]
Does anyone have any idea why the spec has this rule? It's specific to pointers-to-member, so I would suspect that there is some grammatical ambiguity that this resolves, but I honestly haven't the faintest idea what it might be.
This is just a personal opinion.
If &(qualified-id) is allowed as &(unary-expression),
qualified-id has to be an expression, and an expression is expected to have a type
(even if it is incomplete).
However, C++ didn't have a type which denotes a member, had only
a pointer to member.
For example, the following code cannot be compiled.
struct A { int i; };
template< class T > void f( T* );
int main() {
(void) typeid( A::i );
f( &A::i );
}
In order to make &(qualified-id) be valid, the compiler has to hold
a member type internally.
However, if we abandon &(qualified-id) notation, the compiler doesn't need
to handle member type.
As member type was always handled in the form of a pointer to it,
I guess the standard gave priority to simplify the compiler's type
system a little.
Imagine this code:
struct B { int data; };
struct C { int data; };
struct A : B, C {
void f() {
// error: converting "int B::*" to "int*" ?
int *bData = &B::data;
// OK: a normal pointer
int *bData = &(B::data);
}
};
Without the trick with the parentheses, you would not be able to take a pointer directly to B's data member (you would need base-class casts and games with this - not nice).
From the ARM:
Note that the address-of operator must be explicitly used to get a pointer to member; there is no implicit conversion ... Had there been, we would have an ambiguity in the context of a member function ... For example,
void B::f() {
int B::* p = &B::i; // OK
p = B::i; // error: B::i is an int
p = &i; // error: '&i'means '&this->i' which is an 'int*'
int *q = &i; // OK
q = B::i; // error: 'B::i is an int
q = &B::i; // error: '&B::i' is an 'int B::*'
}
The IS just kept this pre-Standard concept and explicitly mentioned that parentheses make it so that you don't get a pointer to member.