Conversion de C++ char* [] en char**
j'ai ce code simple qui compile sans erreurs/avertissements:
void f(int&, char**&){}
int main(int argc, char* argv[])
{
f(argc, argv);
return 0;
}
Et suivant le même code qui ne compile pas:
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;
}
Pourquoi char*[]
peut être converti char**&
dans le premier échantillon et ne peut pas être convertie dans le deuxième exemple? Est-ce important que la taille soit connue au moment de la compilation?
EDIT: je pense qu'il y a 2 conversions nécessaires dans le second cas, et une seule conversion implicite peut être faite par compilateur.
ce code compile:
void f(int&, char**&){}
int main()
{
int argc = 2;
char* temp[] = { "", "", nullptr };
char** argv = temp;
f(argc, argv);
return 0;
}
3 réponses
Jefffrey commentaire les références de la norme, elle est ici:
4.2 conversion tableau-à-pointeur [conv.tableau]
Une lvalue ou rvalue de type "tableau de N T" ou "tableau de l'inconnu lié de T" peuvent être convertis à une valeur de type "pointer vers T". Le résultat est un pointeur vers l' premier élément du tableau.
Et un prvalue est:
une prvalue ("pure" rvalue) est une expression qui identifie temporaire objet (ou un sous-objet de celui-ci) ou est une valeur associée à aucun objet.
vous ne pouvez pas lier une référence non-const à un temporaire.
int& i = int(); // error
char* argv[] = { "", "", nullptr };
// the result of the conversion is a prvalue
char**& test = argv; // error
par conséquent le code suivant sera compilé avec plaisir:
#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;
}
une chose importante sur laquelle j'ai glissé est indiquée dans commentaire de Kanze.
Dans le premier exemple fourni dans l'OP, char* argv[]
et char** argv
sont équivalents. Par conséquent, il n'y a pas de 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
Parce que malgré les apparences, le deuxième argument de main
a
type char**
. Lorsqu'il est utilisé comme la déclaration d'une fonction
argument, une pile de niveau supérieur est réécrit à un pointeur, donc char
*[]
est, en fait, char**
. Cela s'applique uniquement à la fonction
les paramètres, cependant.
char*[]
(comme dans votre deuxième cas) permet de convertir un char**
,
mais les résultats de la conversion (comme toute conversion) est un
R valeur, et ne peut pas être utilisé pour initialiser une référence non-const.
Pourquoi pensez-vous voulez la référence? Si c'est pour modifier le pointeur,
la modification de l' char**
argument main
est un comportement indéfini
(formellement, en C, au moins-je n'ai pas vérifié si C++ est plus
libéral ici). Et bien sûr, il n'y a aucun moyen que vous puissiez
modifier la constante de l'adresse d'un tableau. Et si vous ne voulez pas
pour le modifier, pourquoi utiliser une référence?
le type de temp
char* temp[] = { "", "", nullptr };
char* []
char*[3]
ce dernier ne peut pas être implicitement converti en `char**'.
main
, le type de argv
indépendant char*
array qui est équivalent à char**
j'avoue, c'est source de confusion :)