Как определить конструктор полностью специализированного класса в другом шаблоне класса - PullRequest
6 голосов
/ 04 июля 2019

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

template <typename Outer>
struct ContainingClass {
  template <typename T>
  struct Rule {
    Rule(T value);

    // ... other members ...
  };

  template <>
  struct Rule<void> {
    Rule();

    // ... different members than the non-void Rule<T> ...
  };
};

Я определил конструкторы как для универсального, так и специализированного Rule:

template <typename Outer>
template <typename T>
ContainingClass<Outer>::Rule<T>::Rule(T value) { }

template <typename Outer>
ContainingClass<Outer>::Rule<void>::Rule() { }

Но Clang не нравится конструктор специализированного класса:

error: nested name specifier 'ContainingClass<Outer>::Rule<void>::' for declaration does not refer into a class, class template or class template partial specialization
ContainingClass<Outer>::Rule<void>::Rule() { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

Я озадачен этим, потому что он "ссылается на" ContainingClass<Outer>, что является класс (экземпляр шаблона класса ContainingClass).Я подозреваю, что мне нужно что-то изменить в синтаксисе для этого, но не ясно, что.Как я могу определить этот конструктор?

(Он работает, если я удаляю ContainingClass и помещаю Rule в область имен, но мне нужно, чтобы в Rule были другие вещи, которые зависят от типа OuterЯ мог бы дать Rule свой собственный Outer параметр шаблона, но это сделает вещи более неуклюжими для кода, использующего этот класс, поэтому я хотел бы избежать его, если это возможно. И я знаю, что могу определить конструкторвстроенный в тело класса Rule, но я хотел бы понять, почему отдельное определение не работает.)

В случае, если это имеет значение, я использую как Clang 8.0 в Ubuntu 19.04, так и Apple«clang-1001.0.46.4» на Mac.(Я также попробовал GCC 8.3 в Ubuntu, но в другом месте это не помогло из-за ошибки GCC # 85282 - «явная специализация в области без пространства имен» в определении самого struct Rule<void>.)

Отредактировано для уточнения: Моя ошибка не в том, что специализация template <> struct Rule<void> в пределах ContainingClass.Это предмет ограничения C ++ 14 ( дефект CWG 727 ), который обходится путем добавления фиктивного параметра шаблона, чтобы шаблон был только частично, а не полностью специализированным.Я считаю, что ограничение было снято в C ++ 17, и специализация самого класса Rule прекрасно работает в Clang (хотя в GCC есть ошибка).Так что я думаю, что обходной путь фиктивного параметра не является правильным решением здесь - но, пожалуйста, скажите мне, если я ошибаюсь, и все еще есть ограничения на это в C ++ 17.

1 Ответ

5 голосов
/ 04 июля 2019

Вы не можете объявить в области имен пространства членом специализации шаблона члена шаблона.

Чтобы избежать этого ограничения, вы можете использовать обходной путь, который раньше был необходим для c ++ 14 иранее, который заключается в использовании частичной специализации вместо полной специализации:

template <typename Outer>
struct ContainingClass {
  template <typename T,class=void>
  struct Rule {
    Rule(T value);

    // ... other members ...
  };

  template <class U>
  struct Rule<void,U> {
    Rule();

    // ... different members than the non-void Rule<T> ...
  };
};

template <typename Outer>
template <typename T, typename U>
ContainingClass<Outer>::Rule<T,U>::Rule(T value) { }

template <typename Outer>
template <typename U>
ContainingClass<Outer>::Rule<void,U>::Rule() { }

В C ++ 17 все еще невозможно объявить (член) члена специализации шаблона класса в области пространства имен,см. [temp.expl.spec] / 17 .Этот же абзац существует в стандарте C ++ 14 .

Что изменилось в C ++ 17, так это то, что мы можем объявить специализацию члена внутри определения шаблона включающего класса:

Явная специализация должна быть объявлена ​​в пространстве имен, включающемспециализированный шаблон. [...]

Явная специализация может быть объявлена ​​в любой области, в которой может быть определен соответствующий первичный шаблон. [...]

...