2013-11-13 4 views
6

При попытке выполнить clang-3.4 (скомпилировано из git) не удалось скомпилировать один из моих проектов, жалующихся на неоднозначность при разрешении перегруженных операторов. Я выяснил, что есть два шаблонных оператора, один из которых был объявлен как функция-член, а другой - как не-член, который оба кажется одинаково хорошим.Шаблонное разрешение перегрузки оператора, функция-член против функции без члена

После SSCCE демонстрирует ситуацию:

#include <iostream> 

struct ostr { 
     std::ostream& s; 

     template<class T> 
     ostr& operator<<(const T& x) { s << x; return *this; } 
}; 

struct xy { 
     double x, y; 
}; 

template<class Stream> 
Stream& operator<<(Stream& s, const xy& x) { 
     s << "[" << x.x << ", " << x.y << "]"; 
     return s; 
} 

int main() { 
     ostr os{std::cout}; 
     xy x{4, 5}; 
     os << "Value is: " << x <<"\n"; 
} 

Проект компилируется штраф прежде, и я еще раз проверил эту SSCCE с несколькими компиляторами (gcc 4.5, 4.6, 4.7, 4.8 и clang 3.3), и все они скомпилированных без какой-либо предупреждение (с -Wall -Wextra -pedantic). Все компиляторы были установлены на стандарт C++ 11/C++ 0x. После добавления CTOR к ostr, он составлен хорошо, даже на MSVC 2012 и 2010)

решений как operator<< сек нечленом проявляет неоднозначность во всех составителей (как ожидалось)

После просмотра стандартных проектов (N3242 и N3690) Мне не удалось найти что-либо, улучшающее соответствие функций-членов/операторов, чем не-члены.

Так что я не смог доказать, что clang-3.4 ошибается, и мне интересно, кто прав. Таким образом, мой вопрос:

  • Действительно ли этот код действителен? Должны ли члены-операторы/функции быть лучше, чем не-члены, и это ошибка в clang-3.4?
  • Или все другие компиляторы ошибочны/слишком разрешительны?

Я знаю, что изменение второй operator<< к не-шаблонной функции (с std::ostream вместо параметра шаблона) будет разрешить неоднозначность и работать, как ожидалось, но это не точка здесь.

+2

+1 потому что это интересный вопрос; но я бы еще раз, если бы смог, для вашей ясности и тщательности! – Chowlett

+0

«Должны ли члены-операторы/функции быть лучше, чем не-члены» Нет, но они получают в качестве дополнительного параметра ссылку на тип своего класса только для разрешения перегрузки. – dyp

+0

Может быть связано с [этой ошибкой clang] (http://llvm.org/bugs/show_bug.cgi?id=17075) – dyp

ответ

2

Разрешение перегрузки добавляет дополнительный параметр в функции члена только в целях разрешения перегрузки:

[over.match.funcs]/2

Множество функций-кандидатов может содержать как функции члена и нечлена, подлежащие разрешению в отношении одного и того же списка аргументов. Таким образом, список аргументов и параметров сопоставим в этом гетерогенном наборе, считается, что функция-член имеет дополнительный параметр, называемый параметром неявного объекта , который представляет объект, для которого была вызвана функция-член.

/4

Для не-статических функций-членов, типа параметра неявного объекта является

- «именующая ссылка на резюмеX» для функций, объявленных без исх -квалификатор или с &ref-классификатор

- «Rvalue ссылка на резюмеX» для функций, объявленных с &&реф-классификаторе

, где X является класс которых функция является членом и резюме является резюме -qualification по объявлению функции члена.

Некоторые особые правила следовать, например, чтобы позволить связывание RValue к этому неявному параметру объекта (для вызова функций-членов без рефов-классификатор на rvalues, например ostr{std::cout}<<"hello").


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

template<class T> 
ostr& ostr::operator<<(ostr&, const T&); // F1 

template<class Stream> 
Stream& ::operator<<(Stream&, const xy&); // F2 

После подстановки для os << x, мы получаем же подпись:

ostr& ostr::operator<<(ostr&, const xy&); 
ostr& :: operator<<(ostr&, const xy&); 

Так что только один из «тай-брейкеров» в [более .match.best]/1 может разрешить двусмысленность. Действительно, можно было бы применить, а именно: «F1 более специализирован, чем F2» (или наоборот): частичный порядок шаблонов функций.

N.B. Процедура добавления неявного параметра объекта уточняется в описании частичного заказа [temp.func.order]/3.


Для частичного упорядочения F1 и F2 (как определено выше), мы сначала создать два уникальных типа:

struct unique_T {}; 
struct unique_Stream {}; 

Тогда мы преобразуем F1 в F1', заменив параметр шаблона T с уникальным типом unique_T (и аналогичным образом для F2):

ostr& ostr::operator<<(ostr&, const unique_T&); 
ostr& :: operator<<(unique_Stream&, const xy&); 

Параметры преобразованной функции F1' теперь используются, чтобы попытаться вывести параметры шаблона нетрансформированного F2:

ostr a0; 
unique_T a1; // no reference, no cv-qualifier 
::operator<<(a0, a1) // does template argument deduction succeed? 

// reminder: signature of ::operator<< 
template<class Stream> 
Stream& ::operator<<(Stream&, const xy&); 

Вычет преуспевает для a0Stream = ostr], поэтому тип ostr& из F1 считается быть, по меньшей мере, таким же специализированным, как тип соответствующего первого параметра F2 (Stream&, причем Stream является параметром шаблона). Я не уверен, что происходит со вторым аргументом a1, так как для второго параметра ::operator<< (он имеет тип const xy&) не происходит дедукции.

Теперь мы повторяем процесс с аргументами из F2' и попытаться вывести параметры шаблона из F1:

unique_Stream a0; 
xy a1; 
ostr::operator<<(a0, a1); 

// reminder: signature of ostr::operator<< 
template<class T> 
ostr& ostr::operator<<(ostr&, const T&); 

Здесь нет вычета не возникает для первого аргумента, но это происходит и преуспевает для второго аргумента [ с T = xy].

Я не заключаю, что шаблон функции не является более специализированным. Поэтому разрешение перегрузки должно потерпеть неудачу из-за двусмысленности.

+0

Я уверен? Нет. Пожалуйста, добавьте любой намек, который может помочь прояснить проблему. – dyp

+0

Большое спасибо за шаг за шагом. Вы сделали [over.match.best] намного яснее для меня :). Таким образом, код должен быть недействительным, а clang (после r190470) является единственным правильным? Это определенно кажется таким образом ... – v154c1

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