Как ADL должен работать для этого?
Недавно я столкнулся с проблемой с компилятором clang++ 5.0.0, когда через ADL он не подбирал правильную функцию на Mac (но g ++ делал это правильно на Linux). Я хотел бы знать, является ли это компилятором ИЛИ плохой дизайн класса вообще.
Вот пример кода (исключительно для иллюстрации):
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <template<typename> class E, typename T>
Ops& operator<< (Ops& op, const E<T>& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
int main() {
test::Ops op;
int k = 9;
test::Any<int> a;
op << a;
return 0;
}
Я хотел бы знать, как ADL и вывод аргументов шаблона будут работать поэтапно, чтобы найти лучшее соответствие?
Будет ли какая-либо ситуация для того же "основного тела", в котором функция-член предпочтительнее, чем свободная функция? (Это то, что происходит в сборке продукта)
Заранее спасибо.
2 ответа
Это то, что происходит в деталях и что должен делать каждый компилятор: кандидатная функция шаблона найдена квалифицированным поиском
template <typename T>
test::Ops::operator<<(const T&)
в то время как второй кандидат генерируется через ADL с использованием вывода аргументов шаблона (ср. temp.deduct.conv)
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
После этого срабатывает разрешение перегрузки (ср. 13.3.3), и не член (F1) предпочтительнее, чем член (F2), так как
- F1 и F2 являются специализациями шаблонов функций, а шаблон функций для F1 более специализирован, чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в 14.5.6.2.
и, таким образом, выбран в качестве функции для вызова.
Чтобы ответить на ваш вопрос: это зависит от правил разрешения перегрузки. Будучи функцией-членом или во внутренней области видимости не влияет на результат и что-то вроде
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <typename E>
Ops& operator<< (Ops& op, const E& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
просто вызовет ошибку разрешения перегрузкиuse of overloaded operator '<<' is ambiguous
".
Как плюс: функция-член неверна, даже если она была выбрана: this->val
присваивается нецелочисленный тип.
Эти две функции-кандидата находятся в наборе перегрузки:
// member function template, found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)
// non-member function template, found by ADL
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
При поиске операторов предпочтение не предоставляется членам, а не членам. После подстановки аргумента шаблона обе специализации шаблона функции точно соответствуют (с квалификационными преобразованиями) предоставленным типам аргументов. Но функция, принимающая E<T>
более специализирован, чем принимающий T
, поэтому функция, не являющаяся членом, выбрана по этой причине.
Apple Clang 5.0.0 основан на LLVM clang 3.3svn. Я не могу найти ни одной версии LLVM Clang, которая выбирает функцию-член. Это может быть ошибкой в коде Apple, но, скорее всего, это будет небольшая разница в коде, который вы на самом деле компилируете, или в вашей среде. Вы пытались скомпилировать пример кода с подозрительным компилятором?