2015-10-06 2 views
3

Следующий фрагмент кода не может правильно собрать как с G ++ и Clang ++, независимо от стандартной С ++ требуется (98, 11, 14):друга определение функции состоит в шаблонах класса

$ cat foo.cc 
template <typename T> 
struct foo 
{ 
    friend void bar(){} 
}; 

int main() 
{ 
    foo<int> fi; 
    foo<char> fc; 
} 

, например:

$ clang++-mp-3.7 -std=c++14 foo.cc 
foo.cc:4:15: error: redefinition of 'bar' 
    friend void bar(){} 
      ^
foo.cc:10:13: note: in instantiation of template class 'foo<char>' requested here 
    foo<char> fc; 
      ^
foo.cc:4:15: note: previous definition is here 
    friend void bar(){} 
      ^
1 error generated. 

Я знаю, что могу обойти эту проблему, поставив определение (не объявление) bar из foo, я не прошу обходного. Скорее, я хотел бы понять, что происходит. Поэтому у меня есть два вопроса:

  • Во-первых, я не вижу, где в стандарте это поведение документировано или подразумевается. Я сомневаюсь, что и G ++, и Clang ++ получили бы это неправильно, поэтому я ожидал, что он где-то документирован/подразумевается.

  • Во-вторых, в чем смысл такого поведения? Существует ценность в определении друзей внутри класса (см., Например, Is there any difference if we define friend function inside or outside of class), так что же важно сохранить это поведение. Я имею в виду, не должен ли стандарт воспринимать поведение, как ожидал бы один (I)?

+0

Интересно. Мое наивное предположение состояло бы в том, что должны быть два 'bar', один' foo :: bar' и один 'foo :: bar' (ну, не совсем, но близко) - каждый друг в лексической сфере класс, о котором идет речь. Только функции, не определенные в строке, должны находиться в области охватывающего пространства имен. Но я не понимаю эту часть стандарта достаточно хорошо, чтобы сделать хороший аргумент. – Yakk

+1

Необязательно иметь друга 'foo ', который не зависит от 'T'. Для чего нужна дружба? –

+0

Это потому, что я с удовольствием снял реальный случай. Но на самом деле вы помогли мне понять, что было действительно неправильно в реальной реализации: «bar» снова был затенен («T2») вместо того, чтобы быть тем, как я написал его здесь. Но тогда, поскольку его подлинная подпись действительно принимает аргумент 'foo ', генерируемые подписи для сгенерированных друзей _are_ разные, и все работает так, как ожидалось. Благодаря! – akim

ответ

4

Для каждого T создан новый класс. Таким образом, foo<int> - это один класс, а foo<char> - другой.

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

struct foo_int 
{ 
    friend void bar() {} 
}; 

struct foo_char 
{ 
    friend void bar() {} 
}; 


int main() 
{ 
    foo_int fi; 
    foo_char fc; 
} 

Через friend ключевого слова, которое вы определяете функцию статической глобальную (область пространства имен) с именем bar. И как вы видите здесь, вы делаете это дважды.

+0

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

+0

@akim См. [Здесь] (http://stackoverflow.com/questions/4114126/scope-of-friend-function-in-gcc) для объема функций друга, объявленных в классах. И я не понимаю, о чем должен вас предупредить компилятор? Вы получаете ошибку, которая полностью прекрасна. –

+0

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

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