Reputation: 20780
I have this simple code that compiles without errors/warnings:
void f(int&, char**&){}
int main(int argc, char* argv[])
{
f(argc, argv);
return 0;
}
And next similar code that doesn't compile:
void f(int&, char**&){}
int main()
{
int argc = 2;
char* argv[] = { "", "", nullptr };
f(argc, argv);
//@VS2013 error: cannot convert argument 2 from 'char *[3]' to 'char **&'
//@GCC error: invalid initialization of non-const reference of type 'char**&' from an rvalue of type 'char**'
return 0;
}
Why char*[]
can be converted to char**&
in the first sample and can't be converted in the second sample? Does it matter if the size is known at compile time?
EDIT: I think there are 2 conversions needed in the second case, and only one implicit conversion can be done by compiler.
This code compiles fine:
void f(int&, char**&){}
int main()
{
int argc = 2;
char* temp[] = { "", "", nullptr };
char** argv = temp;
f(argc, argv);
return 0;
}
Upvotes: 16
Views: 5061
Reputation:
Jefffrey's comment references the standard, here it is:
4.2 Array-to-pointer conversion [conv.array]
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.
And a prvalue is:
A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
You cannot bind a non-const reference to a temporary.
int& i = int(); // error
char* argv[] = { "", "", nullptr };
// the result of the conversion is a prvalue
char**& test = argv; // error
Therefore the following code will happily compile:
#include <iostream>
void f(int& argc, char** const& argv){
std::cout << argv[0] << std::endl; // a
}
int main()
{
int argc = 2;
char* argv[] = { "a", "b", nullptr };
f(argc, argv);
return 0;
}
One important thing I glazed over is pointed out in Kanze's comment.
In the first example provided in the OP, char* argv[]
and char** argv
are equivalent. Therefore, there is no conversion.
std::cout << std::is_array<decltype(argv)>::value << std::endl; // false
std::cout << std::is_array<char**>::value << std::endl; // false
std::cout << std::is_array<char*[]>::value << std::endl; // true
std::cout << std::is_same<decltype(argv), char**>::value << std::endl; // true
std::cout << std::is_same<decltype(argv), char*[]>::value << std::endl; // false
Upvotes: 3
Reputation: 7960
The type of temp
in
char* temp[] = { "", "", nullptr };
is not char*[], it's
char*[3]
The latter can't be implicitly converted to `char**'.
In main
, the type of argv
is an unbound char*
array which is equivalent to char**
I admit, it's confusing :)
Upvotes: 0
Reputation: 153919
Because despite appearances, the second argument to main
has
type char**
. When used as the declaration of a function
argument, a top level array is rewritten to a pointer, so char
*[]
is, in fact, char**
. This only applies to function
parameters, however.
A char*[]
(as in your second case) can convert to a char**
,
but the results of the conversion (as with any conversion) is an
rvalue, and cannot be used to initialize a non-const reference.
Why do you want the reference? If it is to modify the pointer,
modifying the char**
argument to main
is undefined behavior
(formally, in C, at least—I've not checked if C++ is more
liberal here). And of course, there's no way you can possibly
modify the constant address of an array. And if you don't want
to modify it, why use a reference?
Upvotes: 9