2013-07-17 3 views
8

У меня есть небольшой кусок кода C++ 11, который g ++ (4.7 или 4.8) отказывается компилировать, утверждая, что вызов конструктора для B2 b2a (x, {P (y)}) неоднозначно. Clang ++ доволен этим кодом, но отказывается компилировать B2 b2b (x, {{P (y)}}), который g ++ отлично скомпилирует!C++ 11 разрешение перегрузки конструктора и initialiser_lists: clang ++ и g ++ не согласны

Оба компилятора прекрасно согласны с конструктором B1 с {...} или {{...}} в качестве аргумента. Может ли любой юрист на языке C++ объяснить, какой компилятор прав (если есть) и что происходит? Код ниже:

#include <initializer_list> 

using namespace std; 

class Y {}; 
class X; 

template<class T> class P { 
public: 
    P(T); 
}; 

template<class T> class A { 
public: 
    A(initializer_list<T>); 
}; 

class B1 { 
public: 
    B1(const X&, const Y &); 
    B1(const X&, const A<Y> &); 
}; 

class B2 { 
public: 
    B2(const X &, const P<Y> &); 
    B2(const X &, const A<P<Y>> &); 
}; 

int f(const X &x, const Y y) { 
    B1 b1a(x, {y}); 
    B1 b1b(x, {{y}}); 
    B2 b2a(x, {P<Y>(y)}); 
    B2 b2b(x, {{P<Y>(y)}}); 
    return 0; 
} 

и ошибки компилятора, лязг:

$ clang++ -stdlib=libc++ -std=c++11 test-initialiser-list-4.cc -o test.o -c 
test-initialiser-list-4.cc:32:6: error: call to constructor of 'B2' is ambiguous 
    B2 b2(x, {{P<Y>(y)}}); 
    ^~~~~~~~~~~~~~~ 
test-initialiser-list-4.cc:26:5: note: candidate constructor 
    B2(const X &, const P<Y> &); 
    ^
test-initialiser-list-4.cc:27:5: note: candidate constructor 
    B2(const X &, const A<P<Y>> &); 
    ^

г ++:

test-initialiser-list-4.cc: In function 'int f(const X&, Y)': 
test-initialiser-list-4.cc:32:21: error: call of overloaded 'B2(const X&, <brace-enclosed initializer list>)' is ambiguous 
    B2 b2(x, {P<Y>(y)}); 
        ^
test-initialiser-list-4.cc:32:21: note: candidates are: 
test-initialiser-list-4.cc:27:5: note: B2::B2(const X&, const A<P<Y> >&) 
    B2(const X &, const A<P<Y>> &); 
    ^
test-initialiser-list-4.cc:26:5: note: B2::B2(const X&, const P<Y>&) 
    B2(const X &, const P<Y> &); 
    ^

Это пахнет как взаимодействие между равномерной инициализацией, синтаксисом списка инициализатора и функцией перегрузкой с шаблонным аргументы (которые я знаю, g ++ довольно жесткие), но мне не хватает юриста по стандартам, чтобы иметь возможность распаковывать то, что должно быть правильным поведением здесь!

+1

Два локальных переменных с именем «» b2b в функции «F» это просто опечатка и не является причиной проблемы, я полагаю ... – astraujums

+0

Действительно! Исправлено ... –

+1

Мне кажется, что создание 'A' из initializer_list и' P' из 'T' должно быть точным совпадением, следовательно, двусмысленностью. Я не могу объяснить, почему компиляторы выбирают разные, чтобы жаловаться. –

ответ

5

Первый код, то, что я думаю, должно произойти. (В дальнейшем я проигнорирую первый параметр, так как нас интересует только второй параметр, первый из которых всегда является точным совпадением в вашем примере). Обратите внимание, что в настоящее время правила применяются в спецификации, поэтому я бы не сказал, что у одного или другого компилятора есть ошибка.

B1 b1a(x, {y}); 

Этот код не может вызвать const Y& конструктора в C++ 11, потому что Y представляет собой совокупность и Y не имеет элемента данных типа Y (конечно) или что-то еще initializable ею (это что-то некрасиво, и он исправляется - на компакт-диске C++ 14 пока нет формулировки, поэтому я не уверен, будет ли в последнем C++ 14 это исправление).

Конструктор с параметром const A<Y>& можно назвать - {y} будет принят в качестве аргумента конструктора A<Y> и инициализирует этот конструктор-х std::initializer_list<Y>.

Следовательно - Второй конструктор успешно вызван.

B1 b1b(x, {{y}}); 

Здесь в основном тот же аргумент имеет значение счетчиков для конструктора с параметром const Y&.

Для конструктора с параметром типа const A<Y>& это немного сложнее. Правило для стоимости преобразования при вычислении перегрузки вычисляет стоимость инициализации std::initializer_list<T>, чтобы каждый элемент скобочного списка был конвертирован в T. Однако ранее мы говорили, что {y} не может быть преобразован в Y (так как это совокупность). Теперь важно знать, является ли std::initializer_list<T> агрегатом или нет. Честно говоря, у меня нет идеи, следует ли это считать агрегатом в соответствии со стандартными библиотечными предложениями.

Если мы возьмем его как неагрегат, тогда мы рассмотрим конструктор копирования std::initializer_list<Y>, который, однако, повторит ту же последовательность тестов (что приведет к «бесконечной рекурсии» при проверке разрешения перегрузки).Поскольку это довольно странно и нереализуемо, я не думаю, что какая-либо реализация принимает этот путь.

Если мы возьмем std::initializer_list, чтобы быть агрегатом, мы будем говорить «нет, конверсия не найдена» (см. Вышеуказанные проблемы с агрегатами). В этом случае, поскольку мы не можем вызвать конструктор инициализатора с единственным списком инициализаторов в целом, {{y}} будет разделен на несколько аргументов, а конструктор (ы) A<Y> будет брать каждый из них отдельно. Следовательно, в этом случае мы получим {y}, инициализируя std::initializer_list<Y> как единственный параметр, который отлично работает и работает как шарм.

Таким образом, в предположении, что std::initializer_list<T> является агрегатом, это нормально, и второй конструктор успешно завершил.

B2 b2a(x, {P<Y>(y)}); 

В этом случае и в следующем случае, мы не имеем совокупный вопрос, как и выше с Y больше, поскольку P<Y> имеет предоставленный пользователем конструктор.

Для конструктора параметров P<Y> этот параметр будет инициализирован {P<Y> object}. Поскольку P<Y> не имеет списков инициализаторов, список будет разбит на отдельные аргументы и вызовет конструктор перемещения P<Y> с объектом rvalue P<Y>.

Для конструктора параметра A<P<Y>>, это то же самое, как выше случае с A<Y> инициализированного {y}: Так как std::initializer_list<P<Y>> может быть инициализирован {P<Y> object}, список аргументов не расщепляется, и, следовательно, скобки используются для инициализатора, что конструктор'S std::initializer_list<T> ,

Теперь оба конструктора работают нормально. Они действуют как перегруженные функции здесь, а их второй параметр в обоих случаях требует определенного пользователем преобразования. Определенные пользователем последовательности преобразования могут сравниваться только в том случае, если в обоих случаях используется одна и та же функция преобразования или конструктор - здесь не так. Следовательно, это неоднозначно в C++ 11 (и на компакт-диске C++ 14).

Обратите внимание, что здесь мы имеем тонкий момент, чтобы исследовать

struct X { operator int(); X(){/*nonaggregate*/} }; 

void f(X); 
void f(int); 

int main() { 
    X x; 
    f({x}); // ambiguity! 
    f(x); // OK, calls first f 
} 

Этого счетчик интуитивный результат, вероятно, будет зафиксирован в той же перспективе с фиксацией агрегированных инициализации странности, упомянутой выше (оба будет называть первую е) , Это реализовано тем, что {x}->X становится преобразованием идентичности (как и X->x). В настоящее время это пользовательское преобразование.

Итак, неопределенность здесь.

B2 b2b(x, {{P<Y>(y)}}); 

Для конструктора с параметром const P<Y>&, мы снова расколоть аргументы и получить {P<Y> object} аргумент, передаваемый в конструктор (ы) P<Y>. Помните, что у P<Y> есть конструктор копирования. Но усложнение здесь заключается в том, что нам не разрешено использовать его (см. 13.3.3.1p4), поскольку для этого потребуется преобразование, определяемое пользователем. Единственный конструктор слева - тот, который принимает Y, но Y не может быть инициализирован {P<Y> object}.

Для конструктора с параметром A<P<Y>>, то {{P<Y> object}} может инициализировать std::initializer_list<P<Y>>, потому что {P<Y> object} конвертируется в P<Y> (иначе, чем с Y выше - Dang, агрегатов).

Итак, второй конструктор, получивший название.


Резюме для всех 4

  • второй конструктор называется успешно
  • в предположении, что std::initializer_list<T> представляет собой совокупность, это прекрасно и называют второй конструктор успешно
  • двусмысленность здесь
  • второй конструктор называется успешно
+0

Спасибо, похоже, что мое замешательство не было полностью необоснованным :) –

 Смежные вопросы

  • Нет связанных вопросов^_^