2010-05-14 3 views
28

В качестве дополнения к this question, что происходит здесь:Инициализация в Структуры C++

#include <string> 
using namespace std; 

struct A { 
    string s; 
}; 

int main() { 
    A a = {0}; 
} 

Очевидно, что вы не можете установить зЬй :: строку к нулю. Может ли кто-нибудь дать объяснение (с ссылками на стандарт C++, пожалуйста) о том, что на самом деле должно произойти здесь? А затем поясните, например):

int main() { 
    A a = {42}; 
} 

Являются ли эти из них определенными?

Еще раз смущающий вопрос для меня - я всегда даю конструкторы своих конструкций, поэтому проблема никогда не возникала раньше.

+1

Шаблон класса 'boost :: array' также является агрегатом. Таким образом, вы можете использовать 'array a = {" foo "," bar "};' с ним, например. Кроме того, мой lazy-construct-array также является агрегатом: http://stackoverflow.com/questions/2662417/c-suppress-automatic-initialization-and-destruction/2662526#2662526 –

+3

Неявные преобразования + агрегаты ... ಠ_ಠ –

+0

@litb, когда я впервые увидел эту особенность «boost :: array», у меня было просветление, сексуальное удовлетворение от ААА. Простые вещи, которые имеют такой смысл, имеют тенденцию делать это со мной. – wilhelmtell

ответ

29

Ваша структура является агрегатом, поэтому для этого применяются обычные правила для агрегатной инициализации. Процесс описан в 8.5.1. В принципе, для этого посвящено всего 8.5.1, поэтому я не вижу причины копировать все это здесь. Общая идея практически такая же, как и в C, только что адаптированная к C++: вы берете инициализатор справа, вы берете член слева и инициализируете элемент с помощью этого инициализатора. Согласно 8.5/12, это будет копия-инициализация.

Когда вы

A a = { 0 }; 

вы в основном копировать инициализацией a.s с 0, то есть для a.s это семантически эквивалентно

string s = 0; 

Вышеприведенные компилируется, потому что std::string конвертируется из const char * указателя , (И это неопределенное поведение, так как нулевой указатель не является веским аргументом в этом случае.)

Ваша версия 42 не будет компилировать по той же самой причине

string s = 42; 

не будет компилировать. 42 не является константой нулевого указателя, а std::string не имеет средств для преобразования из int.

P.S. На всякий случай: обратите внимание, что определение агрегата в C++ не является рекурсивным (в отличие от определения POD, например). std::string не является агрегированным, но он ничего не меняет для вашего A. A по-прежнему является совокупностью.

+0

§ 12.6.1 также имеет значение, как указано в § 8.5.1. 13. – outis

+0

@outis: я просмотрел 12.6.1, и я не мог сразу увидеть, что он добавил к тому, что уже было в 8.5. Каждый раз, когда 12.6.1 имеет дело с агрегатной инициализацией, он, похоже, возвращается к 8.5 :) – AnT

+0

Интересно видеть, что в 'basic_string (size_type n, charT c, const Allocator a = Allocator())' есть причина, почему 'size_type n' не имеет значения по умолчанию. Причина в том, что это плохая идея перегрузить указатель и целое число. Значение 0 (ноль) строго является целым числом, а не указателем, и поэтому вы не сможете построить через перегрузку указателя с нулевым указателем, если, конечно, вы не произнесите явно.Стандарт избегает этой путаницы, требуя тип символа, если вы укажете длину строки при построении строки. – wilhelmtell

8

8.5.1/12 «Заполнители» говорит:

Все Неявные преобразования типов (пункт 4) рассматриваются при инициализации совокупного члена с инициализаторе из инициализатора-лист.

Так

A a = {0}; 

будет инициализирован с NULL char*AndreyT и Johannes не указано иное), и

A a = {42}; 

потерпит неудачу во время компиляции, так как нет неявного преобразования, который будет совпадают с конструктором std::string.

2

Как люди указали, это «работает», потому что строка имеет конструктор, который может принимать 0 в качестве параметра. Если мы скажем:

#include <map> 
using namespace std; 

struct A { 
    map <int,int> m; 
}; 

int main() { 
    A a = {0}; 
} 

, то мы получаем ошибку компиляции, так как класс карты не имеет такого конструктора.

+0

Почему ваш аватар на аватаре не показан в этом ответе? Он скрыт в ответах сообщества? –

+0

@Johannes Тайна! Вы хотите сообщить об этом в качестве ошибки в мета или я? – 2010-05-14 23:26:53

+0

@Neil пойти на это :) –

1

В 21.3.1/9 стандарт запрещает аргумент char* соответствующего конструктора std::basic_string как нулевой указатель. Это должно выбросить std::logic_error, но я еще не видел, где в стандарте есть гарантия того, что нарушение предусловия вызывает std::logic_error.

+0

Если я не ошибаюсь, нарушение предусловия гарантирует неопределенное поведение, а не исключение. –

+1

@James g ++ 4.0.1 на OS X 10.5.8 выдает 'std :: logic_error' во время построения. В 19.1.1 говорится, что это то, что для 'logic_error', но я не могу найти гарантии, что это происходит, когда есть нарушение инварианта или предпосылки. – wilhelmtell

3

0 является пустым указателем константа

S.4.9:

Нулевой константный указатель является неотъемлемой константа (5.19) Rvalue целочисленного типа, который имеет значение нуль.

Нулевой константный указатель может быть преобразован в любой другой тип указателя:

S.4.9:

Нулевой константный указатель может быть преобразован в тип указателя; результат является нулевым значением указателя этого типа

Что вы дали для определения A считаются совокупностью:

S.8.5.1:

Агрегатными представляет собой массив или класс без конструкторов, не объявленных пользователем, без частных или защищенных нестатических элементов данных, базовых классов и виртуальных функций.

Вы определяете положение об инициализации:

S.8.5.1:

Когда агрегат инициализируется инициализатор может содержать инициализатор-пункт, состоящий из скобки заключен, запятой списка из инициализатора-положений для членов совокупного

A содержит член агрегата типа std::string, и к нему применяется предложение инициализатора.

Ваш агрегат копия инициализируется

Когда агрегат содержит (будь то класс или массив) членов типа класса и инициализируется брекет прилагается инициализатора-лист, каждый такой элемент является копией -initialized.

Copy означает, что у вас есть эквивалент 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 «s конструктор const char* не определен как explicit означает, что вы можете сделать это: 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; 
} 
Смежные вопросы