определение функций в заголовочных файлах

Если вы хотите поместить определения функций в файлы заголовков, представляется три различных решения:

  1. отметьте функцию как inline
  2. отметьте функцию как static
  3. помещать функцию в анонимное пространство имен

(До недавнего времени я даже не знал о №1.) Каковы различия между этими решениями, и когда я должен предпочесть, какой? Я в мире только для заголовков, поэтому мне действительно нужны определения в файлах заголовков.

Ответ 1

static и неназванные версии пространства имен становятся одинаковыми: каждый модуль перевода будет содержать свою собственную версию функции, а это означает, что при заданной статической функции f указатель &f будет отличаться в каждой единицы перевода, и программа будет содержать N разных версий f (больше кода в двоичном формате).

Это не правильный подход, чтобы обеспечить функции в заголовке, это обеспечит п различную (точно равную) функцию. Если функция содержит static локали, тогда будет N разных static локальных переменных...

EDIT: чтобы сделать это более явным: если вы хотите предоставить определение функции в заголовке, не нарушая одно правило определения, правильный подход заключается в том, чтобы сделать функцию inline.

Ответ 2

Насколько я знаю, в заголовочных файлах могут быть определены только inline и шаблонные функции.

static функции устаревают, и вместо этого следует использовать функции, определенные в неназванном пространстве имен (см. 7.3.1.1 p2). Когда вы определяете функцию в неназванном пространстве имен в заголовке, каждый исходный код, включающий этот заголовок (прямо или косвенно), будет иметь уникальное определение (см. 7.3.1.1 p1). Поэтому функции не должны определяться в неназванном пространстве имен в файлах заголовков (только в исходных файлах).

Стандарт, на который даны ссылки, относится к стандарту С++ 03.

РЕДАКТИРОВАТЬ:

Следующий пример демонстрирует, почему функции и переменные не должны быть определены в неназванное пространство имен в заголовках:

ops.hpp содержит:

#ifndef OPS_HPP
#define OPS_HPP
namespace
{
int a;
}
#endif

dk1.hpp содержит:

#ifndef DK1_HPP
#define DK1_HPP
void setValue();
void printValue();
#endif

dk1.cpp содержит:

#include "dk1.hpp"
#include "ops.hpp"
#include <iostream>

void setValue()
{
    a=5;
}
void printValue()
{
    std::cout<<a<<std::endl;
}

dk.cpp содержит:

#include "dk1.hpp"
#include "ops.hpp"
#include <iostream>

int main()
{
    // set and print a
    setValue();
    printValue();

    // set and print it again
    a = 22;
    std::cout<<a<<std::endl;

    // print it again
    printValue();
}

Скомпилируйте следующим образом:

g++ -ansi -pedantic -Wall -Wextra dk.cpp dk1.cpp

и выход:

5
22
5

ops переменная a отличается для исходного файла dk1.cpp и dk.cpp

Ответ 3

static функции (эквивалентные анонимному пространству имен) получают разные копии для каждого ТУ. Если функция является повторной, это в основном идентично (некоторые компиляторы могут иметь разницу на уровне сборки), но если это не так, то для каждой ТУ она будет иметь разные статические данные. Встроенными функциями являются folded-, то есть они имеют только одну копию статических данных для каждого TU.

Ответ 4

Вы можете рассмотреть обертывание методов в классе вместо пространства имен. Объявите эти методы как статические и удалите конструктор класса, чтобы подчеркнуть, что это не объект, для которого создается экземпляр.

struct FooNamespace
{
    FooNamespace() = delete;

    static FooMethod1() {
        ...
    }
    static FooMethod2() {
        ...
    }

};

Вы получаете то же общее поведение, что и в пространстве имен, только с одной реализацией.