Классы и свободные функции в namespace
От: kwas Россия  
Дата: 23.05.06 13:46
Оценка: 6 (1)
Почему

// Foo.h
namespace bar {
    class Foo {
    public:
        Foo ();
    };
}

// Foo.cpp
#include "Foo.h"
using namespace bar;

Foo::Foo () {
    //...
}

можно, а

// Baz.h
namespace bar {
    void baz ();
}

// Baz.cpp
#include "Baz.h"
using namespace bar;

void baz () {
    // ,,,
}

нельзя?
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re: Классы и свободные функции в namespace
От: WhiteDev  
Дата: 23.05.06 14:03
Оценка:
Здравствуйте, kwas, Вы писали:

K>Почему


K>
K>// Foo.h
K>namespace bar {
K>    class Foo {
K>    public:
K>        Foo ();
K>    };
K>}

K>// Foo.cpp
K>#include "Foo.h"
K>using namespace bar;

K>Foo::Foo () {
K>    //...
K>}
K>

K>можно, а

K>
K>// Baz.h
K>namespace bar {
K>    void baz ();
K>}

K>// Baz.cpp
K>#include "Baz.h"
K>using namespace bar;

K>void baz () {
K>    // ,,,
K>}
K>

K>нельзя?

Потому как в первом случае из namespace'а берется имя класса, а во втором не понятно это определение функции из namespace'а или просто свободная функция. Вродь так. Поправьте меня если я не прав
Re: Классы и свободные функции в namespace
От: Lorenzo_LAMAS  
Дата: 23.05.06 14:17
Оценка:
А почему ты решил, что нельзя?
Of course, the code must be complete enough to compile and link.
Re[2]: Классы и свободные функции в namespace
От: kwas Россия  
Дата: 23.05.06 14:26
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:

L_L>А почему ты решил, что нельзя?


Потому что компилироваться — компилируется, но при попытке вызова bar::baz() — не линкуется (не находит bar::baz()). Похоже, что baz() в Baz.cpp рассматривается как определение еще одной свободной функции. Почему компилятор не рассматривает определение baz() в baz.cpp как определение bar::baz()?
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[2]: Классы и свободные функции в namespace
От: Ubivetz Украина  
Дата: 23.05.06 14:27
Оценка:
Здравствуйте, WhiteDev, Вы писали:
K>>
K>>// Baz.h
K>>namespace bar {
K>>    void baz ();
K>>}

K>>// Baz.cpp
K>>#include "Baz.h"
K>>using namespace bar;

K>>void baz () {
K>>    // ,,,
K>>}
K>>

K>>нельзя?

WD>Потому как в первом случае из namespace'а берется имя класса, а во втором не понятно это определение функции из namespace'а или просто свободная функция. Вродь так. Поправьте меня если я не прав

Надо так:
// Baz.h
namespace bar
{
  void baz();
}
// Baz.cpp
#include "Baz.h"
namespace bar
{
   void baz()
   {
   }
}
// или
bar::baz()
{
}
Эх, люблю выпить и переспать с кем нибудь!
Но чаще выходит перепить с кем — нибудь и выспаться...
Re[2]: Классы и свободные функции в namespace
От: Максим2006 Беларусь  
Дата: 23.05.06 14:30
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:

L_L>А почему ты решил, что нельзя?


Нельзя, потому что компилер не знает чего от него хотят: то ли функцию из нэймспейса определить, то ли новую написать.
Код с классом тоже компилиться не будет, если такой же объявить вне нэймспейса.
Re[3]: Классы и свободные функции в namespace
От: kwas Россия  
Дата: 23.05.06 14:30
Оценка:
Здравствуйте, Ubivetz, Вы писали:

U>Надо так:


Это-то понятно — точное указание определяемой функции. Интересно, почему нельзя так, как в исходном посте
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[3]: Классы и свободные функции в namespace
От: kwas Россия  
Дата: 23.05.06 14:42
Оценка:
Здравствуйте, Максим2006, Вы писали:

М>Нельзя, потому что компилер не знает чего от него хотят: то ли функцию из нэймспейса определить, то ли новую написать.

М>Код с классом тоже компилиться не будет, если такой же объявить вне нэймспейса.

В том-то и дело, что пример с функциями — компилируется, но не линкуется, а пример с классами — не скомпилируется.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[4]: Классы и свободные функции в namespace
От: Максим2006 Беларусь  
Дата: 23.05.06 14:59
Оценка:
Здравствуйте, kwas, Вы писали:

K>Здравствуйте, Максим2006, Вы писали:


М>>Нельзя, потому что компилер не знает чего от него хотят: то ли функцию из нэймспейса определить, то ли новую написать.

М>>Код с классом тоже компилиться не будет, если такой же объявить вне нэймспейса.

K>В том-то и дело, что пример с функциями — компилируется, но не линкуется, а пример с классами — не скомпилируется.

это от того, что локальное объявление перекрывает объявление более высокого уровня. То есть эта функция скомпилировалась как локальная для этого файла, а значит, функция из нэймспейса осталась не определённой, отсюда и ошибка линковки.
Re: Классы и свободные функции в namespace
От: MuTPu4  
Дата: 23.05.06 18:59
Оценка: 13 (4)
Здравствуйте, kwas, Вы писали:

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

Для начала немного теории:

scope и declarative region (3.3/1):

Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity. In general, each particular name is valid only within some possibly discontiguous portion of program text called its scope.


Еще относительно деклараций (3.3/3):

The names declared by a declaration are introduced into the scope in which the declaration occurs


using-directive (7.3.4/1):

A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

То есть, имена, вводимые using-directive видны как декларации только в ходе поиска имен.

Теперь рассмотрим случай со свободными функциями.
namespace test
{
  void foo( ); //1
}

void foo( ) //2
{}

int main( void )
{}

В данном случае у нас декларация 1 вводит имя foo в scope пространства имен test, а декларация 2, которая является определением (8.4), вводит имя foo в global scope (3.3.5/3). Поскольку декларации 1 и 2 находятся лексически в разных scope, они относятся к различным функциям (13.2/1):

Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations

Вместо этого после декларации 2 имя foo из global scope скрыто именем foo из namespace test при поиске имен в namespace test (3.3.7/1):

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class


namespace test
{
  void foo( ); //1
}

using namespace test;
void foo( ) //2
{}

int main( void )
{}

В виду указанной особенности using-directive в данном контексте она не может расцениваться как декларация foo, поэтому, как и в первом примере, декларации 1 и 2 никак не связаны.

Ради интереса, можно еще рассмотреть пример с using-declaration:
namespace test
{
  void foo( ); //1
}

using test::foo;
void foo( ) //2
{}

int main( void )
{}

Этот код не корректен (7.3.3/11):

If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.


Кроме этого, существует еще возможность использовать квалификацию для определения функции вне пространства имен, где она объявлена (7.3.1.2/2):

Members of a named namespace can also be defined outside that namespace by explicit qualification (3.4.3.2) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration’s namespace.

(8.3/1):

A declarator-id shall not be qualified except for the definition of a member function (9.3) or static data member (9.4) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id.

Видимо, этот пункт можно рассматривать как исключение из (13.2/1).
Это можно проиллюстрировать следующим кодом:
namespace ex1
{
  void baz( void );
}
void ex1::baz( void ){} //OK

//обратите внимание, 
//этот случай очень напоминает пример в исходном сообщении.
namespace ex21
{
  namespace ex22
  {
    void baz( void );
  }
}
using namespace ex21;
void ex22::baz( void ){} //OK

namespace ex31
{
  namespace ex32
  {
    void baz( void );
  }
  using ex32::baz;
}
void ex31::baz( void ){} //Error

namespace ex41
{
  void baz( void );
}
namespace ex42
{
  void ex41::baz( void ){} //Error
}

int main( void )
{}


Теперь относительно функций-членов.
В дополнение к уже процитированному (8.3/1) на всякий случай добавлю (9.3/2):

A member function may be defined (8.4) in its class definition, in which case it is an inline member function (7.1.2), or it may be defined outside of its class definition if it has already been declared but not defined in its class definition. A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition.

и (9.3/5):

If the definition of a member function is lexically outside its class definition, the member function name shall be qualified by its class name using the :: operator.

Вот несколько примеров:
struct test
{
  void baz( void );
};
void test::baz( void ){} //OK

namespace ex2
{
  struct test2
  {
    void baz( void );
  };
}
using namespace ex2;
void test2::baz( void ){} //OK

namespace ex3
{
  struct test3
  {
    void baz( void );
  };
}
using ex3::test3;
void test3::baz( void ){} //OK

namespace ex41
{
  namespace ex42
  {
    struct test4
    {
      void baz( void );
    };
  }
}
void ex41::ex42::test4::baz( void ){} //OK

//баг GCC 3.4.2
struct test5
{
  void baz( void );
};
namespace ex5
{
  void test5::baz( void ){} //Error
}

int main( void )
{}


Итого, наиболее существенным здесь является то, что при декларации функции с неквалифицированным declarator-id действует простое правило, основанное на лексическом расположении декларации в определенном scope, а при использовании квалифицированного declarator-id (в том числе out of class определения функций-членов), сам квалификатор является предметом поиска имен.
Re[2]: Классы и свободные функции в namespace
От: kwas Россия  
Дата: 24.05.06 07:57
Оценка:
Здравствуйте, MuTPu4, Вы писали:

Исчерпывающе! Спасибо!
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.