2016-08-18 3 views
8

Я был мастером, чтобы подтвердить пример на стр. 91 Эффективного современного C++, и я столкнулся с тем, что кажется странной проблемой. Этот кодC++ noexcept декларация изменения вычета шаблона

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
    std::cout << "container version" << std::endl; 
} 

template<> 
void doStuff<int>(int& x, int& y) noexcept { 
    std::cout << "int version" << std::endl; 
} 

int main() { 
    vector<int> v1 = {1, 2, 3}; 
    vector<int> v2 = {4, 5, 6}; 
    int x = 5; 
    int y = 6; 
    doStuff(x, y); 
    doStuff(v1, v2); 
} 

дает мне ошибку, как

error: request for member ‘front’ in ‘a’, which is of non-class type ‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {

Таким образом, кажется, как верхняя версия DoStuff вызывается, даже если a.front() и b.front() должна быть возвращая ссылки на ints. Если я удалю все noexcept объявления из кода, я получаю ожидаемый результат.

Это с gcc 5.4.

Что я делаю неправильно?

Благодаря

+1

Помните, что шаблоны являются функция compile.time. Когда определена первая версия функции 'doStuff', компилятор ничего не знает о специализации для' int'. –

+1

Существует также проблема, что 'doStuff' не был объявлен, когда он впервые используется в спецификации noexcept. – aschepler

ответ

7

Проблема заключается в том, когда поиск имени в этой точке:

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
//           ^^^^^^^ 

просто найти один doStuff(): шаблон функции. Специализация пока не объявлена, поэтому она не рассматривается.

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

namespace N { 
    struct adl { }; 

    void doStuff(adl, int& , int&) noexcept { 
     std::cout << "int version" << std::endl; 
    } 

    template<typename C> 
    void doStuff(adl, C& a, C& b) noexcept(noexcept(doStuff(adl{}, a.front(), b.front()))) { 
     std::cout << "container version" << std::endl; 
    } 
} 

template <class C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(N::adl{}, a, b))) 
{ 
    doStuff(N::adl{}, a, b); 
} 
+1

Действительно ли перегрузка 'int' должна быть первой? Не является ли 'doStuff' в спецификаторе noexcept' зависимым символом, то есть он не разрешен до экземпляра? – John

+0

Возможно, это не сработает для 'C = std :: vector >'. – aschepler

+0

@aschepler Исправлено. – Barry

2

шаблона не перегружает. Ваша специализация для doStuff<int> не является перегрузкой doStuff<C>, это специализация. Таким образом, разрешение перегрузки не учитывает его, будет рассмотрен экземпляр шаблона, если оригинал выбран с помощью разрешения перегрузки. Заменить специализацию с перегрузкой (без шаблона, взяв два int& S)

void doStuff(int& a, int& b) noexcept; 
Смежные вопросы