#c #templates #language-specifications #one-definition-rule
#c #шаблоны #язык-спецификации #одно определение-правило
Вопрос:
Примечание: следующий код незаконен, но соответствующий компилятор не обязан отклонять его (а некоторые этого не делают).
В библиотеке, с которой я работаю, у меня есть объявление функции шаблона для 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
. Но (по причине, я полагаю, мне не нужно объяснять) это не для начала.
Комментарии:
1. Что должно означать это последнее определение
Foo
? Абсолютно не понятно, что вы пытаетесь сделать. Предоставьте более подробную информацию.2. Почему бы не указать
int(*)(void)
указатель на функцию в качестве параметра шаблона наBar
вместо класса C?template <int(*FOO)()> int Bar() { return Something(FOO()); }
. Тогда пользователь может указать, какую функцию вызывать напрямую, а не косвенно, специализируясьFoo
наMyClass
. И тогда указатель на функцию можно было бы заменить классом functor, если предпочтительнее.3. Я думаю, что шаблоны бесплатных функций обычно следует объявлять
inline
, чтобы обойти ODR.4. Не уверен насчет ODR, но код, похоже, нарушает 14.7.3 / 6: «Если шаблон … является явно специализированным, тогда эта специализация должна быть объявлена перед первым использованием этой специализации, что привело бы к неявному созданию экземпляра … »
5. Мысль: у вас нет специализации, которая должна быть включена перед определением Bar, непосредственно перед первым использованием Bar<MyClass> должно быть достаточно, потому что точка создания есть. Т.Е. наличие специализации, объявленной (даже не определенной) раньше,
main()
было бы законно, я полагаю. Тоже не вариант?
Ответ №1:
Есть ли способ сделать эту работу также легальной?
Да, объявите специализацию перед ее использованием. Простое изменение порядка специализации и main в вашем файле сделало бы это в приведенном вами примере. Однако в приведенном примере вам все равно придется убедиться, что ни один другой TU не использовал специализацию без ее объявления.
Как правило, эти специализации должны быть объявлены в заголовке.
В пределах того, что, как представляется, находится в пределах вашего примера (т. Е. Без радикальных изменений), другого способа заставить это работать нет.
Комментарии:
1. Это можно было бы заставить работать в случае, с которым я столкнулся, но не очень хорошо обобщает. (смотрите комментарий, который я добавил во 2-м блоке кода)
2. @BCS: «Идеальная» ситуация в вашем комментарии категорически невозможна: вещи должны быть объявлены, прежде чем их можно будет использовать, и объявление для этой функции не может отсутствовать в TU, где она используется.
3. @BCS: Сравните с ситуацией попытки void f() { this_function_not_declared(); } без объявления вызываемой функции в этом TU.
4. Да, они должны быть объявлены до их использования, но что я хочу для идеального решения, так это использовать шаблонную функцию, которая только (и, в этом TU, только будет) объявлена, но не определена .
5. @BCS: Это нормально. Объявляйте перед использованием, определите это один раз.
Ответ №2:
Это незаконно, поскольку невозможно специализировать функции шаблона.
Вы могли бы сделать что-то вроде этого :
template< typename T >
struct foo
{
static int doSomething() {return 0;}
};
template< >
struct foo<int>
{
static int doSomething() {return 5;}
};
Комментарии:
1. Конечно, шаблоны функций могут быть специализированными, просто не частично специализированными.
2. Мне нравится, как
do
он окрашен в синий цвет. 🙂3. иногда мне хочется, чтобы люди не голосовали, если они не понимают… -1 что говорит @qauamrana.
Ответ №3:
Это компилирует ссылки и запускается как с 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. Ваше описание — это именно то поведение, которое мне нужно. OTOH, хотя он может компилироваться и запускаться, это незаконный код: permalink.gmane.org/gmane.comp.compilers.llvm.bugs/10472 Я думаю, что и GCC, и clang отвергнут это.
2. @BCS: Я думаю, что этот код легален , потому что нет определения
Foo
того, когда компилируется main. Если есть, gcc 4.5.2 возвращается с простой ошибкой множественного определения.3. Если Bar не является шаблонизированным и выполняется вызов Foo для прямого указания типа, то это генерирует сообщение об ошибке (т. е. это незаконно). Если я чего-то не упускаю, это не должно иметь значения с точки зрения законности (т. Е. Оба являются незаконными), потому что в любом случае вызов Foo должен быть обработан до определения Foo<MyClass> .