Это очень интересный случай list initialization
.
bar({1, 2, 3})
bar({{1, 2, 3}})
Это случай инициализации списка копий, здесь временный объект создается путем инициализации списка копий, и к нему привязана ссылка const.
Чтобы понять, как работают функции bar()
, необходимо понять, как работает инициализация списка, и правила разрешения его перегрузки. Итак, давайте go один за другим
1009 *
std::vector<double> vec = {0, 1};
Это также инициализация списка (инициализация списка копирования),
std::vector
имеет следующий конструктор,
vector( std::initializer_list<T> init,
const Allocator& alloc = Allocator() );
std::initializer_list<T>
имеет наивысший приоритет по сравнению с другим конструктором в разрешении перегрузки и из-за этого разрешения перегрузки выбирает его.
Другой случай,
std::vector<double> vec = {0, 1};
std::vector<double> other = {vec};
Это другой случай, теперь фигурные скобки имеют только один элемент , и это точно тип переменной other
(которая является std::vector<double>
), в соответствии с list initialization
правилами разрешения перегрузки, более подробную информацию можно найти в специальных правилах для разрешения перегрузки . Разрешение перегрузки не выбирает std::initializer_list<T>
конструктор , скорее оно выбирает копировать конструктор , это Точный ранг ранга , см. Об этом в ссылке, приведенной выше. Теперь этот случай поможет понять, что происходит за экраном.
Давайте посмотрим на следующий код,
std::vector<double> other = {{0, 1}};
Согласно предыдущему обсуждению, компилятор должен сначала создать временную std::vector<double>
, а затем инициализировать перемещение переменную other
но, к счастью, компилятор этого не делает, а выполняет следующее, Поэтому скорее создайте временную std::vector<double>
, а затем переместите, инициализируйте переменную other
, чтобы оптимизировать временный объект и напрямую инициализируйте other
переменную, вызвав std::initializer_list
конструктор с {0, 1}
в качестве аргумента конструктора.
Наконец, последний случай,
std::vector<std::vector<double>> nestedVec = {{0, 1}};
Компилятор сначала создаст временный std::vector<double>
, и это означает, что express логически становится std::vector<std::vector<double>> nestedVec = {std::vector<double>{0, 1}};
, а затем разрешение перегрузки выберет std::initializer_list
конструктор. Таким образом, обучение заключается в том, что выражение {{1, 2, 3}}
способно инициализировать std::vector<double>
, а также std::vector<std::vector<double>>
и создавать неоднозначность в вызове функции bar()
, это также означает, что std::initializer_list
с одним элементом следует использовать осторожно.
Теперь, чтобы устранить неоднозначность, можно сделать следующее, измените вызов функции,
bar({1, 2, 3});
bar({{1, 2, 3}, {}}); //put an empty element.
или используйте несколько фигурных скобок, как описано в вопросе
bar({{{1, 2, 3}}});
, или сначала создайте переменную и передайте эту переменную в качестве аргумента функции, а не передавая std::initializer_list
в качестве аргумента функции.
следующий код продемонстрирует то, что я объяснил,
#include <iostream>
#include <vector>
using std::cout;
template <class T>
class Container{
public:
Container(){
cout<< "Default contructor.\n"<< __PRETTY_FUNCTION__<< '\n';
}
Container(const Container& ){
cout<< "Copy contructor.\n"<< __PRETTY_FUNCTION__<< '\n';
}
Container(Container&& ){
cout<< "Move contructor.\n"<< __PRETTY_FUNCTION__<< '\n';
}
Container(const std::initializer_list<T>& ){
cout<< "std::initializer_list contructor.\n"<< __PRETTY_FUNCTION__<< '\n';
}
};
int main(int , char *[]){
std::cout<<"1 --- ";
Container<double> dObj; //1
cout<< '\n';
std::cout<<"2 --- ";
[[maybe_unused]] Container<double> cObj = {dObj}; //2
cout<< '\n';
std::cout<<"3 --- ";
[[maybe_unused]] Container<double> lObj = {{0, 1}}; //3
cout<< '\n';
std::cout<<"4 --- ";
[[maybe_unused]] Container<Container<double>> nObj = {{0, 1}}; //4
cout<< '\n';
}
Вывод:
1 --- Default contructor.
Container<T>::Container() [with T = double]
2 --- Copy contructor.
Container<T>::Container(const Container<T>&) [with T = double]
3 --- std::initializer_list contructor.
Container<T>::Container(const std::initializer_list<_Tp>&) [with T = double]
4 --- std::initializer_list contructor.
Container<T>::Container(const std::initializer_list<_Tp>&) [with T = double]
std::initializer_list contructor.
Container<T>::Container(const std::initializer_list<_Tp>&) [with T = Container<double>]