Может ли это быть юридически сделано в C ++? - PullRequest
2 голосов
/ 04 мая 2011

Примечание: следующий код является недопустимым , но соответствующий компилятор не обязан отклонять его (а некоторые этого не делают).

В библиотеке, с которой я работаю, у меня есть объявление функции шаблона для Foo и определение функции шаблона для Bar в foobar.h:

template<class C> int Foo();

template<class C> int Bar() {
  return Something(  Foo<C>()  );
}

Смысл в том, что другой кодмог бы использовать это так:

#include "foobar.h"

int main() {
  Bar<MyClass>();
  return 0;
}

// Ideally, the usage (above) and the definition (below)
// would/could be in different translation units. 

template<> int Foo<MyClass>() { return 5; }

Вопрос: Есть ли способ сделать эту работу, которая также является законной?


Проблема заключается в том (если я правильно понимаю), что, несмотря на компиляцию, это технически незаконно: оно нарушает ODR , потому что как явная специализация, так и использование Bar<MyClass> считаются определениями, несмотря на то, что в случае использования нет тела для работы.

Причины, по которым я хочу использовать этот шаблон для параметризации Foo, заключаются в том, что в результате руководства по стилюЯ обязан следовать, единственный способ гарантировать, что что-нибудь лексикВключение до определения Bar должно быть включено в foobar.h.Но (по причине, которую я ожидаю, мне не нужно объяснять), это не стартер.

Ответы [ 3 ]

3 голосов
/ 04 мая 2011

Есть ли способ сделать эту работу также законной?

Да, объявите специализацию перед использованием. Просто поменяйте местами порядок специализации и main в вашем файле, чтобы сделать это в приведенном вами примере. Однако в этом примере вам все равно придется убедиться, что другие TU не использовали специализацию, не объявив ее.

Как правило, эти специализации должны быть объявлены в заголовке.

В пределах того, что кажется ограниченным вашим примером (то есть никаких радикальных изменений), нет другого способа заставить эту работу.

1 голос
/ 04 мая 2011

Компилирует ссылки и работает как с VS2008, так и с gcc 4.5.2:

template<class C> int Foo();

int Something(int i ){ return i; }

template<class C> int Bar() {
    return Something(  Foo<C>()  );
}

class MyClass{};
class FooClass{};

// Update:
// Declaration of a specialisation.
template<> int Foo<MyClass>();

int Zoo(){
    return Something(  Foo<MyClass>()  );
}

#include <iostream>

int main() {
    std::cout << Bar<MyClass>() << std::endl;
    std::cout << Zoo() << std::endl;
    std::cout << Bar<FooClass>() << std::endl;
    return 0;
}

// Definitions of specialisations.
template<> int Foo<MyClass>() { return 5; }
template<> int Foo<FooClass>() { return 6; }

Вывод:

5
5
6

Причина, по которой это работает, несмотря на то, чтобудучи объявленным, шаблонная функция Foo имеет два обязательных определения, доступных для компоновщика.
Во время компиляции, когда компилятор видит определение main, он может создавать специализации Bar, но не может создавать специализацииFoo.В этом случае он просто создает вызовы функций для Foo<MyClass>() и Foo<FooClass>().
Позже компилятор находит специализации Foo, которые он компилирует, оставляет в объектном файле, но в данный момент ничего больше не делает.Когда компоновщик запускается, он находит все, что ему нужно.

Обновление:
Так что я не знаю, почему это незаконно, но по какой-то причине он, похоже, компилируется и запускается.

1 голос
/ 04 мая 2011

Это не разрешено законом, поскольку невозможно специализировать функции шаблона.

Вы могли бы сделать что-то вроде этого:

template< typename T >
struct foo
{
  static int doSomething() {return 0;}
};
template< >
struct foo<int>
{
  static int doSomething() {return 5;}
};
...