Как приложение к этому вопросу, что продолжается здесь:
#include
using namespace std;
struct A {
string s;
};
int main() {
A a = {0};
}
Очевидно, Вы не можете установить станд.:: представьте в виде строки для обнуления. Кто-то может дать объяснение (поддержанный ссылками на Стандарт C++) о том, что, как на самом деле предполагается, происходит здесь? И затем объясните, например):
int main() {
A a = {42};
}
Действительно ли любой из них четко определен?
Еще раз смущающий вопрос для меня - я всегда даю моим конструкторам структур, таким образом, проблема никогда не возникала прежде.
Ваша структура является агрегатом , поэтому для него работают обычные правила агрегатной инициализации. Процесс описан в 8.5.1. По сути, ему посвящена вся версия 8.5.1, поэтому я не вижу смысла копировать все это здесь. Общая идея практически такая же, как и в C, но адаптирована к C ++: вы берете инициализатор справа, вы берете член слева, и вы инициализируете член этим инициализатором. Согласно 8.5 / 12, это должна быть инициализация копирования .
Когда вы выполняете
A a = { 0 };
, вы в основном инициализируете как
с 0
, то есть для как
это семантически эквивалентно
string s = 0;
Выше компилируется, потому что std :: string
конвертируется из указателя const char *
. (И это неопределенное поведение, поскольку нулевой указатель не является допустимым аргументом в этом случае.)
Ваша версия 42
не будет компилироваться по той же причине, по которой
string s = 42;
не будет компилироваться. 42
не является константой нулевого указателя, а std :: string
не имеет средств для преобразования из типа int
.
P.S. На всякий случай: обратите внимание, что определение агрегата в C ++ не является рекурсивным (в отличие, например, от определения POD). std :: string
не является агрегатом, но он ничего не меняет для вашего A
. A
по-прежнему является агрегатом.
В 21.3.1 / 9 стандарт запрещает аргументу char *
соответствующего конструктора std :: basic_string
быть нулевым указателем. Это должно вызвать ошибку std :: logic_error
, но мне еще предстоит увидеть, где в стандарте гарантируется, что нарушение предварительного условия вызывает ошибку std :: logic_error
.
8.5.1 / 12 «Агрегаты» говорят:
Все неявные преобразования типов (пункт 4) учитываются при инициализации составного члена с помощью инициализатора из списка инициализаторов.
Таким образом,
A a = {0};
будет инициализирован NULL char *
(как указано AndreyT и Johannes ), а
A a = {42};
завершится ошибкой во время компиляции поскольку не существует неявного преобразования, которое соответствовало бы конструктору std :: string
.
0 является константой нулевого указателя
S.4.9:
Константа нулевого указателя - это интегральное постоянное выражение (5.19) rvalue целого типа, которое оценивается в нулю.
Константа нулевого указателя может быть преобразована в любой другой тип указателя:
S.4.9:
Константа нулевого указателя может быть преобразована в тип указателя; результатом будет значение нулевого указателя этого типа
Что вы дали? типа
То, что вы дали для определения A
, считается агрегатом:
S.8.5.1:
Агрегат - это массив или класс без объявленных пользователем конструкторов, без приватных или защищенных нестатических членов данных, базовых классов и виртуальных функций.
Вы указываете условие инициализации:
S.8.5.1:
Когда агрегат инициализируется, инициализатор может содержать условие инициализации, состоящее из заключенного в скобки, разделенного запятыми списка условий инициализации для членов агрегата
A
содержит член агрегата типа std::string
, и к нему применяется условие инициализации.
Ваш агрегат инициализирован копией
Когда агрегат (будь то класс или массив) содержит члены типа класса и инициализируется заключенным в скобку инициализатором-списком, каждый такой член инициализируется копией.
Копирующая инициализация означает, что вы имеете эквивалент std::string s = 0
или std::string s = 42
;
S.8.5-12
Инициализация, которая происходит при передаче аргументов, возврате функции, выбрасывании исключения (15.1), обработке исключение (15.3), и закрытых скобками списках инициализаторов (8.5.1), называется копирующей инициализацией и эквивалентна форме T x = a;
std::string s = 42
не будет компилироваться, потому что неявное преобразование отсутствует, std::string s = 0
будет компилироваться (потому что неявное преобразование существует), но приведет к неопределенному поведению.
конструктор std::string
для const char*
не определен как явный
, что означает, что вы можете сделать следующее: std::string s = 0
Просто чтобы показать, что вещи действительно копируются-инициализируются, вы можете сделать этот простой тест:
class mystring
{
public:
explicit mystring(const char* p){}
};
struct A {
mystring s;
};
int main()
{
//Won't compile because no implicit conversion exists from const char*
//But simply take off explicit above and everything compiles fine.
A a = {0};
return 0;
}
Как отмечают люди, это "работает", потому что у string есть конструктор, который может принимать 0 в качестве параметра. Если мы скажем:
#include <map>
using namespace std;
struct A {
map <int,int> m;
};
int main() {
A a = {0};
}
то получим ошибку компиляции, так как класс map не имеет такого конструктора.