2013-02-28 3 views
4

Я столкнулся с тем, что для меня выглядит как несогласованность компилятора C++. В следующем примере кодапроблемы с шаблоном друга класса шаблона

#include <vector> 
namespace test { 
    class A : std::vector<int> 
    { 
    template<typename F> 
    friend void bar(A const&a, F f) { for(auto i:a) f(i); } 
    template<int K, typename F> 
    friend void foo(A const&a, F f) { for(auto i:a) if(i&K) f(i); } 
    }; 
} 
int sum(test::A const&a) 
{ 
    int s=0; 
    foo<2>(a,[&s](int i) { s+=i; }); // <-- error here 
    bar (a,[&s](int i) { s+=i; }); // <-- but not here 
    return s; 
} 

GCC (4.7.0, используя зЬй = C++ 11) жалуется на «foo не был объявлен в этой области» (и предлагает в качестве альтернативы test::foo), но, к счастью компилирует использование от bar в следующей строке. Теперь оба, foo и bar вводятся в пространство имен test через их объявление friend, поэтому они не должны действительно присутствовать в глобальном пространстве имен.

Q1 Я ошибаюсь, или это новый поворот C++ 11, или gcc misbehaving?

Конечно, проблему можно избежать, если я просто ввел использование директив в глобальное пространство имен. Тем не менее, если я A шаблон,

#include <vector> 
namespace test { 
    template<typename T> 
    class A : std::vector<T> 
    { 
    template<typename F> 
    friend void bar(A const&a, F f) { for(auto i:a) f(i); } 
    template<int K, typename F> 
    friend void foo(A const&a, F f) { for(auto i:a) if(i&K) f(i); } 
    }; 
} 
using test::foo;   // does not avoid compilation error 
using test::bar;   // does not avoid compilation error 
int sum(test::A<int> const&a) 
{ 
    int s=0; 
    foo<2>(a,[&s](int i) { s+=i; }); 
    bar (a,[&s](int i) { s+=i; }); 
    return s; 
} 

GCC жалуется снова. Либо (без директив using), что «foo не был объявлен в этой области» (но снова с радостью компилирует bar, хотя не предлагает test::foo) или (с директивой using), что « не было объявлено» (и то же самое для test::bar) в точке using.

Q2 Это выглядит для меня как ошибку компиляции, так как ни с или без using директивы можно назвать test::foo. Или, может быть, я что-то про C++, что я пропустил?

Наконец, я попытался переместить определение друга вне класса как в

namespace test { 
    template<typename T> 
    class A : std::vector<int> 
    { 
    template<int K, typename F> 
    friend void foo(A const&a, F f); 
    template<typename F> 
    friend void bar(A const&a, F f) { for(auto i:a) f(i); } 
    }; 

    template<int K, typename T, typename F> 
    void foo(A<T> const&a, F f) { for(auto i:a) if(i&K) f(i); } 

} 
using test::foo; 

когда НКУ снова жалуется, на этот раз утверждая, что void test::foo(const test::A<T>&, F) используется, но не определен ... Так Q3, что случилось?

Ответы на любые приветственные подзадачи.

ответ

3

Q1:

Могу ли я ошибаюсь, или это новый поворот C++ 11, или GCC Сбой в?

Нет, это нормальное поведение. Из пункта 14.8.1/8 C++ 11 стандарта на:

Для простых имен функций, аргумент зависимого поиска (3.4.2) применяется даже тогда, когда имя функции не видно в пределах вызова , Это связано с тем, что вызов по-прежнему имеет синтаксическую форму функции call (3.4.1). Но когда используется шаблон функции с явными аргументами шаблона, вызов не имеет правильной синтаксической формы, если нет шаблона функции с этим именем, видимым в точке вызова. Если такое имя не отображается, вызов не является синтаксически корректным, а зависящий от аргумента поиск не применяется . Если какое-либо такое имя видимо, применяется зависимый от аргумента поиск, а дополнительные шаблоны функций могут быть найдены в других пространствах имен. [Пример:

namespace A { 
    struct B { }; 
    template<int X> void f(B); 
} 
namespace C { 
    template<class T> void f(T t); 
} 
void g(A::B b) { 
    f<3>(b); // ill-formed: not a function call 
    A::f<3>(b); // well-formed 
    C::f<3>(b); // ill-formed; argument dependent lookup 
    // applies only to unqualified names 
    using C::f; 
    f<3>(b); // well-formed because C::f is visible; then 
    // A::f is found by argument dependent lookup 
} 

-end пример]


Q2:

Это выглядит для меня как ошибку компиляции, так как ни с или без использования директивы я могу вызвать test :: foo. Или, может быть, я что-то про C++, что я пропустил?

Если ваш класс становится класс шаблон который вы никогда не экземпляр, то компилятор не будет выполнять поиск имени второй фазы, которая будет происходить при инстанцировании A<>, так что это не будет его никогда не узнает, что есть два friend функции, объявленные в нем.

Если введены, например, явная конкретизация шаблона перед тем в using декларации, вы должны видеть вещи меняется:

template class test::A<int>; 

В качестве альтернативы, вы можете просто изменить определение A так, что оно только объявляет и не определяет два шаблона функций friend и предоставляет определение вне класса для этих шаблонов функций. Наверное, это то, что вы на самом деле пытались сделать. Но...

Q3:

НКУ снова жалуется, на этот раз утверждая, что недействительным тест :: Foo (сопзЬ тест :: &, F) используется, но никогда не определял ... Так что же случилось?

Проблема заключается в том, что вы не объявляя в качестве друга и ту же функцию, что вы позже определения: уведомление, что функция вы определили принимает один дополнительный аргумент (T). Сосредоточьте свое заявление, и вы увидите программу компиляции:

namespace test 
{ 
    template<typename T> 
    class A : std::vector<int> 
    { 
     template<int K, typename C, typename F> 
     //    ^^^^^^^^^^ (can't use T here, it would shadow 
     //       the class's template parameter) 
     friend void foo(A<C> const&a, F f); 
    }; 

    template<int K, typename C, typename F> 
    void foo(A<C> const&a, F f) 
    { for(auto i:a) if(i&K) f(i); } 
} 

using test::foo; // Just don't remove this, or we will be back in Q1 ;-) 

ЗАКЛЮЧЕНИЕ:

Таким образом, после всех необходимых изменений, это то, как ваша программа будет выглядеть следующим образом:

#include <vector> 

namespace test 
{ 
    template<typename T> 
    class A : std::vector<T> 
    { 
     template<typename F, typename C> 
     friend void bar(A<C> const&a, F f); 

     template<int K, typename F, typename C> 
     friend void foo(A<C> const&a, F f); 
    }; 

    template<typename F, typename C> 
    void bar(A<C> const&a, F f) { for(auto i:a) f(i); } 

    template<int K, typename F, typename C> 
    void foo(A<C> const&a, F f) { for(auto i:a) if(i&K) f(i); } 
} 

using test::foo; 
using test::bar; 

int sum(test::A<int> const& a) 
{ 
    int s=0; 
    foo<2>(a,[&s](int i) { s+=i; }); 
    bar (a,[&s](int i) { s+=i; }); 

    return s; 
} 
+0

«Должен выглядеть» - это слишком сильно, потому что «использование» может не быть тем, что действительно хочет OP - в некоторых случаях вы действительно хотите, чтобы ADL произошла, и важно понять, что это такое и как оно работает. –

+0

@ DanielFrey: Я изменил «должен» на «волю». –

+0

Кроме того, вы отвечаете на Q2, немного странно, поскольку компилятор всегда * анализирует * код. Во всяком случае, ваш ответ весьма полезен, поэтому: +1 –

1

Ваша проблема и ответ на ваши вопросы называется ADL и правилами для применения. Это не ново в C++ 11, и это не проблема с GCC.

Q1: У вас есть параметр a типа test::A (в первом примере), следовательно, ADL (аргумент зависит поиск) ищет методы в пространстве имен test, но только для не-шаблон вызывает. Вот почему foo<2> (вызов шаблона) не найден и bar есть.

Q2: Ответил после Q3, см. Ниже.

Q3: Ваше определение функции для test::foo не определяет функцию, которую вы объявили как друг test::A<T>.Измените его на

namespace test 
{ 
    template<typename T> 
    class A; 

    template<int K, typename F,typename T> 
    void foo(A<T> const&a, F f); 

    template<typename T> 
    class A : std::vector<int> 
    { 
    template<int K, typename F,typename U> 
    friend void foo(A<U> const&a, F f); 
    template<typename F> 
    friend void bar(A const&a, F f) { for(auto i:a) f(i); } 
    }; 

    template<int K, typename F,typename T> 
    void foo(A<T> const&a, F f) { for(auto i:a) if(i&K) f(i); } 
} 
using test::foo; 

Q2: Подобно Q3, вы можете исправить это следующим образом:

#include <vector> 
namespace test { 
    template<typename T> 
    class A; 

    template<typename F,typename T> 
    void bar(A<T> const&a, F f); 
    template<int K, typename F,typename T> 
    void foo(A<T> const&a, F f); 

    template<typename T> 
    class A : std::vector<T> 
    { 
    template<typename F,typename U> 
    friend void bar(A<U> const&a, F f); 
    template<int K, typename F,typename U> 
    friend void foo(A<U> const&a, F f); 
    }; 

    template<typename F,typename U> 
    void bar(A<U> const&a, F f) { for(auto i:a) f(i); } 
    template<int K, typename F,typename U> 
    void foo(A<U> const&a, F f) { for(auto i:a) if(i&K) f(i); } 
} 
using test::foo; 
using test::bar; 
int sum(test::A<int> const&a) 
{ 
    int s=0; 
    foo<2>(a,[&s](int i) { s+=i; }); 
    bar (a,[&s](int i) { s+=i; }); 
    return s; 
} 

Энди уже объяснил, почему ваш оригинальный пример не работает.

+0

uhm, 'bar' является функцией шаблона. – Walter

+0

@Walter. Правильно, отредактировано. Кроме того, я добавлю дополнительные сведения о Q2/Q3, так как есть несколько подробностей, которые я думаю ... –

+0

Я пробовал это (** Q3 **), но получил «слишком много шаблонов параметров шаблона» – Walter

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