Clang не замечает параметры шаблона по умолчанию - PullRequest
0 голосов
/ 24 января 2019

Фон

В соответствии со стандартом C ++ при прямом объявлении типа шаблона с параметрами шаблона по умолчанию каждый из них может появляться только в объявлении one .Например:

// GOOD example
template <class T = void>
class Example;    // forward-declaration

template <class T>
class Example {}; // definition
// GOOD example
template <class T>
class Example;    // forward-declaration

template <class T = void>
class Example {}; // definition
// BAD example
template <class T = void>
class Example;    // forward-declaration

template <class T = void> // ERROR: template parameter redefines default argument
class Example {}; // definition

Проблема

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

// foo.hpp, bar.hpp, baz.hpp, etc.
template <class T>
class Example;
// example.hpp
template <class T = void>
class Example {};

и, как и ожидалось, все работает хорошо везде ... кроме лязга!Я сузил проблему до следующего:
В clang, если шаблон класса имеет параметры по умолчанию, но они не объявлены в первом прямом объявлении этого класса и при объявлении экземпляра этого класса угловые скобки не указаны , clang игнорирует параметр по умолчанию и выдает ошибку «нет жизнеспособного конструктора или руководство по выводу для аргументов шаблона ...».

Пример

// GOOD example
template <class T>
class Example;

template <class T = void>
class Example {};

int main() {
    Example e; // error: no viable constructor or deduction guide for deduction of template arguments of 'Example'
}
  • Перемещение = void в предварительную декларацию решает проблему, но для меня это нецелесообразно, так как мои forward-decls находятся в разных файлах, и я не знаю, какой из них появится первым.(Также очень проблематично, так как мои значения по умолчанию будут в каком-то непонятном файле в глубине кодовой базы)
  • Изменение Example e; на Example<> e; устраняет проблему, но не жизнеспособно для меня, так как я являюсь разработчиком библиотеки ия не хочу, чтобы все мои пользователи набирали <> после моих занятий.
  • Добавление файла прямого объявления example_fwd.hpp с одним предварительным объявлением и включение его вместо прямого объявления каждый раз, когда исправляет проблему, но яЯ хотел бы избежать этого, если есть лучшее решение.

Вопрос

Кто прав в этом случае: clang или другие компиляторы?Это ошибка компилятора?Как я могу обойти эту проблему (кроме частичных решений, которые я описал выше)?Я нашел # 10147 (и связанные с ним вопросы stackoverflow), но он касается параметров шаблона шаблона и также помечен как исправленный более года назад.

Edit

Thisвыглядит как ошибка, и теперь сообщается об отслеживателе ошибок LLVM ( # 40488 ).

Ответы [ 3 ]

0 голосов
/ 24 января 2019

Стандарт не делает различий, определяется ли аргумент шаблона по умолчанию в определении или объявлении шаблона.

Поскольку Clang принимает код, когда аргумент по умолчанию появляется в объявлении, но не в определении, по крайней мере одно из этих двух вариантов поведения является неправильным. Учитывая [over.match.class.deduct] /1.1.1:

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

, мне хочется сказать, что Clang должен использовать аргумент шаблона по умолчанию.

Я думаю, что вы можете избежать этой ошибки, следуя обычной практике:

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

  2. Определить аргументы по умолчанию в этом файле предварительной декларации

  3. Также включите этот файл в заголовочный файл, который обеспечивает определение шаблона.

В качестве примера см. iosfwd: libstdc ++ / iosfwd

0 голосов
/ 24 января 2019

С учетом следующего:

[temp.param] / 12 - Набор стандартных аргументов шаблона, доступных для использования, получается путем слиянияаргументы по умолчанию из всех предыдущих объявлений шаблона аналогичны аргументам функции по умолчанию [ Пример :

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;

эквивалентно

template<class T1 = int, class T2 = int> class A;

- конец примера ]

Аргументы по умолчанию, доступные для

template <class T>
class Example;

template <class T = void>
class Example {};

, будут аргументами по умолчанию в определении Example.Приведенные выше две декларации будут эквивалентны одной декларации

template <class T = void>
class Example {};

, которая позволит эффективно выполнять Example e.

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

Example() -> Example<>;
0 голосов
/ 24 января 2019

Я не знаю, кто прав, но ...

Как я могу обойти эту проблему (кроме частичных решений, которые я описал выше)?

Как насчетдобавить следующее правило вывода?

Example() -> Example<>;

Следующий код компилируется (очевидно, C ++ 17) с использованием g ++ и clang ++

template <class T>
class Example;

template <class T = void>
class Example {};

Example() -> Example<>;

int main() {
    Example e;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...