2015-08-20 2 views
4

У меня есть следующий код:Что такое правильный способ инициализировать переменную в C++

bool c (a == b); 

и

bool c {a == b}; 

где а и Ь некоторые переменные одного типа.

Я хочу знать, что в чем разница в двух предыдущих инициализации и какой из них следует выбирать в каких условиях? Любая помощь будет оценена по достоинству.

ответ

9

Обе формы: direct initialization.

Использование фигурных скобок {} для инициализации для сужения конверсий и генерирует ошибку, если такое преобразование происходит. В отличие от (). (gcc is buggy in this regard и нуждается в -Werror=narrowing опции компилятора генерировать ошибку, когда происходит сужение.)

Другого использование фигурных скобок {} для равномерной инициализации: инициализировать оба типа и без конструкторов, использующих один и тот же синтаксис, например:

template<class T, class... Args> 
T create(Args&&... args) { 
    T value{std::forward<Args>(args)...}; // <--- uniform initialization + perfect forwarding 
    return value; 
} 

struct X { int a, b; }; 
struct Y { Y(int, int, int); }; 

int main() { 
    auto x = create<X>(1, 2); // POD 
    auto y = create<Y>(1, 2, 3); // A class with a constructor. 
    auto z = create<int>(1);  // built-in type 
} 

Единственным недостатком использования фигурных фигурных скобок {} является его взаимодействие с ключевым словом auto. auto выводит {} как std::initializer_list, который является известным вопросом, см. "Auto and braced-init-lists".

+0

Таким образом, в данном случае, это не имеет значения, что мы используем ? Хотя в тех случаях, когда мы делаем много времени для int или когда есть вероятность потери данных, он должен вызывать исключение/ошибку? –

+1

@cruskal, если '==' перегружен, чтобы возвращать что-то другое, кроме 'bool',' {} 'генерирует ошибку. –

+0

Кроме того, остерегайтесь сочетания инициализаторов, сопоставленных с 'std :: initializer_list' в конструкторах, например' std :: vector c {a, b} 'vs' std :: vector c (a, b) ' –

2

Первый - это прямая инициализация стиля C++ 03. Вторая - это прямая инициализация стиля C++ 11, она дополнительно проверяет сужение конверсий. Herb Sutter рекомендует следующее в новом коде:

auto c = <expression>; 

или когда вы хотите совершить конкретный типа Т:

auto c = T{<expression>}; 

Один известного недостатка в фигурных скобках, когда T некоторый класс с перегруженным конструктором, где один конструктор получает STD :: initializer_list в качестве параметра, станд :: вектор, например:

auto v = std::vector<int>{10}; // create vector<int> with one element = 10 
auto v = std::vector<int>(10); // create vector<int> with 10 integer elements 
+1

Вторая такая глупая рекомендация. Что это дает вам? Единственное, что он делает, это наложить больше ограничений на 'T'. – juanchopanza

+1

@juanchopanza В основе статьи Саттера лежит следующее: «Рассмотрите объявление локальных переменных auto x = type {expr}; когда вы хотите явно передать тип. Это самодокументируется, чтобы показать, что код явно запрашивает преобразование, он гарантирует, что переменная будет инициализирована, и это не позволит случайное неявное сужение преобразования. Только когда вы хотите явное сужение, используйте() вместо {} .' – TartanLlama

+0

Хотя он продолжает говорить: «Жюри по-прежнему не знает, стоит ли рекомендовать эту оптовую торговлю, поскольку мы все еще пытаемся это сделать, но он предлагает некоторые преимущества, и я предлагаю вам попробовать его некоторое время и посмотреть, хорошо ли это работает для вас. ' – TartanLlama

1

Теперь у нас есть пяти форм инициализации. Итог:

T x = expression; 
T x = (expression); 
T x ( expression); 
T x = { expression }; 
T x { expression }; 

Каждая из форм имеет свои особенности. :)

Например, давайте предположим, что у вас есть следующие объявления в глобальном пространстве имен

int x; 

void f(int x) { ::x = x; } 
int g() { return x ; } 
long h() { return x; } 

затем в основной вы можете написать

int main() 
{ 
    int x (g()); 
} 

Этот код компилируется успешно.

Однако программист по ошибке сделал опечатку

int main() 
{ 
    int x; (g()); 
     ^^ 
} 

Oops! Этот код также компилируется успешно. :)

Но если программист напишет

int main() 
{ 
    int x = (g()); 
} 

, а затем сделать опечатку

int main() 
{ 
    int x; = (g()); 
     ^^ 
} 

, то в этом случае код не будет компилироваться.

Предположим, что программист решил сначала установить новое значение для global variabel x перед инициализацией локальной переменной.

Так он писал

int main() 
{ 
    int x (f(10), g()); 
} 

Но этот код не компилируется!

Пусть; s вставить знак равенства

int main() 
{ 
    int x = (f(10), g()); 
} 

Теперь код успешно компилируется!

А как насчет брекетов?

Ни этот код

int main() 
{ 
    int x { f(10), g() }; 
} 

ни этот код

int main() 
{ 
    int x = { f(10), g() }; 
} 

компилирует! :)

Теперь программист решил использовать функцию Н(), он писал

int main() 
{ 
    int x (h()); 
} 

и его код успешно компилируется. Но через какое-то время он решил использовать брекеты

int main() 
{ 
    int x { h() }; 
} 

Упс! Его компилятор выдает сообщение об ошибке

error: non-constant-expression cannot be narrowed from type 'long' to 'int' in initializer list

Программа решила использовать спецификатор типа auto. Он попробовал два подхода

int main() 
{ 
    auto x { 10 }; 
    x = 20; 
}  

и

int main()  
{ 
    auto x = { 10 }; 
    x = 20; 
}  

и ... некоторые компиляторы составил первую программу, но не собрали вторую программу и некоторые компиляторы не компилировать обе программы. :)

А как насчет использования decltype?

Например, программист написал

int main() 
{ 
    int a[] = { 1, 2 }; 
    decltype(auto) b = a; 
}  

И его компилятор выдал ошибку!

Но когда программист приложена в скобках, как этот

int main() 
{ 
    int a[] = { 1, 2 }; 
    decltype(auto) b = (a); 
}  

код компилируется успешно! :)

Теперь программист решил узнать ООП. Он написал простой класс

struct Int 
{ 
    Int(int x = 0) : x(x) {} 
    int x; 
}; 

int main() 
{ 
    Int x = { 10 };  
}  

и его код успешно скомпилирован. Но pogrammer уже известно, что существует функция спецификатор explicit и он решил использовать его

struct Int 
{ 
    explicit Int(int x = 0) : x(x) {} 
    int x; 
}; 

int main() 
{ 
    Int x = { 10 };  
}  

Oops! Его компилятор выдал ошибку

error: chosen constructor is explicit in copy-initialization 

Программист решил удалить знак присваивания

struct Int 
{ 
    explicit Int(int x = 0) : x(x) {} 
    int x; 
}; 

int main() 
{ 
    Int x { 10 };  
}  

и его код, скомпилированный успешно! :)