Можно ли установить разные модификаторы доступа к специализациям шаблона члена класса (и к специализациям членов шаблона класса)?

  1. Можно ли установить разные модификаторы доступа к специализациям шаблона члена класса? Пример кода (не компилируется):

    class SimpleClass
    {   
    public:
        template <typename T>
        void Method();
        template <>
        void Method<char>();
    protected:
        template <>
        void Method<int>();
    protected:
        template <>
        void Method<float>();
    };
    
    • Подвопрос: можно ли установить разные модификаторы доступа к специализациям конструктора шаблона класса? Пример кода (не компилируется):

      class SimpleClass
      {
      public:
          template <typename T>
      SimpleClass(T);
          template <>
          SimpleClass<char>(char);
      protected:
          template <>
          SimpleClass<int>(int);
      private:
          template <>
          SimpleClass<float>(float);
      };
      
  2. Можно ли установить разные модификаторы доступа к специализациям члена шаблона класса? Пример кода (не компилируется):

    template <typename T>
    class ClassTemplate
    {   
    public:
        void Method();
        template <>
        void Method<char>();
    protected:
        template <>
        void Method<int>();
    protected:
        template <>
        void Method<float>();
    };
    
    • Подвопрос: можно ли устанавливать разные модификаторы доступа к специализациям конструктора шаблона класса? Пример кода (не компилируется):

      template <typename T>
      class ClassTemplate
      {
      public:
          ClassTemplate(T);
          template <>
          ClassTemplate<char>(char);
      protected:
          template <>
          ClassTemplate<int>(int);
      private:
          template <>
          ClassTemplate<float>(float);
      };
      

Чтобы быть более конкретным: в 1) я ищу обходной путь С++ 03 для такого кода С++ 11:

class SimpleClass
{
public:
    template <typename T>
    SimpleClass(T);

    template <typename T>
    void Method();
};

template <>
SimpleClass::SimpleClass<char>(char);
template <>
SimpleClass::SimpleClass<int>(int) = delete;

template <>
void SimpleClass::Method<char>();
template <>
void SimpleClass::Method<int>() = delete;

В 2) я ищу обходной путь C++03 для следующего кода C++11:

template <typename T>
class ClassTemplate
{
public:
    ClassTemplate(T);

    void Method();
};

template <>
ClassTemplate<char>::ClassTemplate(char);
template <>
ClassTemplate<int>::ClassTemplate(int) = delete;

template <>
void ClassTemplate<char>::Method();
template <>
void ClassTemplate<int>::Method() = delete;

person Constructor    schedule 20.03.2014    source источник


Ответы (4)


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

template <bool C, typename T = void>
using only_if = typename std::enable_if <C, T>::type;

template <typename A, typename B>
using eq = typename std::is_same <A, B>::type;

class SimpleClass1
{
public:
    template <typename T, only_if <!eq <T, int>{} && !eq <T, float>{}, int> = 0>
    SimpleClass1() { }
protected:
    template <typename T, only_if <eq <T, int>{}, int> = 0>
    SimpleClass1() { }
protected:
    template <typename T, only_if <eq <T, float>{}, int> = 0>
    SimpleClass1() { }
};

class SimpleClass2
{
public:
    template <typename T, only_if <!eq <T, int>{} && !eq <T, float>{}, int> = 0>
    SimpleClass2(T) { }
protected:
    template <typename T, only_if <eq <T, int>{}, int> = 0>
    SimpleClass2(T) { }
private:
    template <typename T, only_if <eq <T, float>{}, int> = 0>
    SimpleClass2(T) { }
};

template <typename T>
class ClassTemplate1
{
public:
    template <typename U, only_if <!eq <U, int>{} && !eq <U, float>{}, int> = 0>
    void Method() { }
protected:
    template <typename U, only_if <eq <U, int>{}, int> = 0>
    void Method() { }
protected:
    template <typename U, only_if <eq <U, float>{}, int> = 0>
    void Method() { }
};

template <typename T>
class ClassTemplate2
{
public:
    template <typename U, only_if <!eq <U, int>{} && !eq <U, float>{}, int> = 0>
    void Method(U) { }
protected:
    template <typename U, only_if <eq <U, int>{}, int> = 0>
    void Method(U) { }
protected:
    template <typename U, only_if <eq <U, float>{}, int> = 0>
    void Method(U) { }
};

Не знаю, где все это может пригодиться :-) Как бы то ни было:

  • Я старался сделать все перегрузки конструктора/метода взаимоисключающими, чтобы избежать двусмысленностей и проблем с разными спецификаторами доступа, которые могут быть сложными. Это затрудняет обобщение на большее количество типов. Псевдоним шаблона поможет в общем случае/случай по умолчанию (который дополняет все остальные).

  • Однако это не совсем соответствует тому, что вы описали в вопросе. Эти методы обеспечивают строгое равенство типов, поэтому не допускают неявных преобразований. Вместо этого вы можете попробовать std::is_convertible, но тогда вы откроете дверь для двусмысленностей.

  • Весь код компилируется как таковой, но я не пытался на самом деле использовать классы, поэтому я не знаю, что может произойти.

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

  • Снова взглянув на код, я думаю, что ClassTemplate мало чем отличается (или совсем не отличается) от SimpleClass. ClassTemplate1 не может иметь аргумент шаблона по умолчанию, потому что это было бы неоднозначно.

person iavr    schedule 20.03.2014
comment
Весь этот вопрос немного похож на проблему XY, но +1 за ее решение! - person Sam Cristall; 20.03.2014
comment
@SamCristall О, я не знал этого термина, но теперь я знаю трудный путь :-) - person iavr; 20.03.2014
comment
@SamCristall Да, я думаю, вы правы насчет проблемы XY. :-) - person Constructor; 20.03.2014
comment
SimpleClass1 использовать нельзя, вы правы. Вот почему я использовал конструктор с одним параметром в своем вопросе. - person Constructor; 20.03.2014

Мы можем сделать это с помощью SFINAE, но, к сожалению, нам нужны is_same и enable_if. К счастью, ни один из них не требует языка C++11! Поэтому мы можем просто использовать их реализации для собственных нужд:

template <typename A, typename B>
struct is_same {
  static const bool value = false;
};

template<typename A>
struct is_same <A, A> {
  static const bool value = true;
};

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

Затем мы объединяем их, используя методы enable_if для удаления SFINAE, которые вам не нужны. Конструктору/методу шаблона нужен трюк. Нам нужно, чтобы первый аргумент был типом для вывода аргумента. Мы добавляем второй фиктивный аргумент с единственной целью удаления метода, если тип не поддерживается вами. При необходимости вы можете добавить перегрузки для большего количества типов. Для Method() мы можем снова применить фиктивный аргумент, а T будет задано явно, поэтому нет необходимости в каких-либо выводах:

class SimpleClassConstructor {
public:
  template <typename T>
  SimpleClassConstructor(T value, 
                         typename enable_if<is_same<T, char>::value, T>::type enabler = 0) {
  }
  template <typename T>
  void Method(typename enable_if<is_same<T, char>::value, T>::type enabler = 0) {
  }
};

Для класса-шаблона ~~мы можем использовать точно такую ​​же методологию, но мы можем использовать результат enable_if напрямую, так как нам не нужен вывод аргументов.~~ Чтобы удовлетворить требования специального удаления методов, мы можем переместить методологию enable_if к параметру шаблона. Это позволит SFINAE мягко удалить метод (вместо отключения всего класса в случае сбоя enable_if):

template <typename T>
class SimpleClassTemplate {
public:
  template <typename Enable = enable_if<is_same<T, char>::value, T>::type>
  SimpleClassTemplate(T value) {

  }
};

Для теста попробуйте следующее:

int main() {
  char a = 0;
  SimpleClassTemplate<char> A1(a); // OK
  SimpleClassConstructor A2(a); // OK

  A2.Method<char>(); // OK
  A2.Method<int>(); // compilation error!

  int b = 0;
  SimpleClassTemplate<int> B1(b); // compilaton error!
  SimpleClassConstructor B2(b); // compilation error!
}
person Sam Cristall    schedule 20.03.2014
comment
Остается случай отсутствия входных параметров. - person iavr; 20.03.2014
comment
@iavr Я не понимаю - не могли бы вы быть более конкретным? - person Sam Cristall; 20.03.2014
comment
Если для конструктора (конструктора по умолчанию) или метода нет входного параметра, вы не можете иметь enable_if (или что-либо еще) в круглых скобках. Вопрос относится к случаю метода шаблона без параметров (где параметр шаблона должен быть явно указан вызывающей стороной). - person iavr; 20.03.2014
comment
@iavr У меня есть такой случай, просто не в моих тестах, добавлю. - person Sam Cristall; 20.03.2014
comment
См. также мой ответ и предыдущий. - person iavr; 20.03.2014
comment
@iavr Смотрите мое редактирование. Я рассмотрел метод шаблона без параметров. - person Sam Cristall; 20.03.2014
comment
Хммм... Хорошо, это короче моего решения (все внутри template <...>). Но я не уверен, что это сработает в случае вариативных параметров (имея другой параметр со значением по умолчанию после вариативных). - person iavr; 20.03.2014
comment
Спасибо. Ваше решение в порядке, но оно не позволяет использовать явное создание экземпляра: template class SimpleClassTemplate<int>;. - person Constructor; 20.03.2014
comment
@Constructor Посмотрите мою новую реализацию, которая устраняет эту проблему. - person Sam Cristall; 20.03.2014
comment
О, я думаю, вам не нужно было удалять свой старый вариант, он был хорош. Теперь у вас есть почти точное решение @iavr. Но спасибо за ваши усилия. - person Constructor; 20.03.2014
comment
@Constructor Основная особенность моего кода заключается в том, что он совместим с С++ 03, чего нет в этом решении (хотя нельзя сказать, что его нельзя было легко изменить, чтобы он был) - person Sam Cristall; 20.03.2014
comment
enable_if, is_same и другие черты типа были реализованы задолго до С++ 11 примерно так, как вы показали в своем ответе, поэтому решение @iavr можно считать совместимым с С++ 03. - person Constructor; 20.03.2014
comment
@Constructor На самом деле я имел в виду синтаксис using, а не материал type_traits - person Sam Cristall; 20.03.2014
comment
Как я вижу, этот синтаксис используется в решении @iavr только для сокращения кода и не несет реальной логической нагрузки. - person Constructor; 20.03.2014

Для конструктора или метода по умолчанию без входных параметров я думаю, что ответа на специализацию шаблона в определении класса достаточно. Он использует только std::enable_if и std::is_same, которые легко определить.

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

template <bool C, typename T = void>
using only_if = typename std::enable_if <C, T>::type;

template <typename A, typename B>
using eq = typename std::is_same <A, B>::type;

class SimpleClass {
public:
    template <typename T, only_if <!eq <T, int>{}, int> = 0>
    SimpleClass(T) { ... }

    template <typename T, only_if <!eq <T, int>{}, int> = 0>
    void Method(T) { ... }
    // ...
};
person iavr    schedule 20.03.2014
comment
Спасибо. Одно замечание: вы забыли void перед Method. Это решает 1), но не 2), насколько я понимаю. Я прав? А как насчет решения общей задачи? - person Constructor; 20.03.2014
comment
Спасибо, исправлено. Я думаю, что вы перепутали проблемы, потому что каждая из (1) и (2) содержит два примера, которые относятся к разным проблемам. Я думаю, мне ясно, что то, что здесь относится к одному (или нескольким) параметрам, и мой предыдущий ответ не содержит параметров. Я что-то упускаю? - person iavr; 20.03.2014
comment
Я имею в виду проблему шаблона класса, а не шаблона члена класса. - person Constructor; 20.03.2014
comment
О, теперь я вижу. В (2) у вас есть шаблон класса. Ну мой предыдущий ответ именно такой случай, только без параметров. Я думаю, что template <only_if <!eq <T, int>{}, int> = 0> SimpleClass(T) { ... }; должно быть хорошо. В противном случае добавьте фиктивный параметр: template <typename D = T, only_if <!eq <D, int>{}, int> = 0> SimpleClass(T) { ... };. - person iavr; 20.03.2014
comment
Хороший. Второе решение лучше, потому что оно позволяет использовать явное создание экземпляров: template class SimpleClass<int>;. Можно глупый вопрос? Почему вы добавляете точку с запятой после определения метода? - person Constructor; 20.03.2014
comment
@Constructor Извините, без причин, так много опечаток ... :-) Сейчас я исправляю ответ. - person iavr; 20.03.2014
comment
Все в порядке, это был всего лишь вопрос. :-) А как насчет решения общей проблемы - специализации членов с разными спецификаторами доступа? - person Constructor; 20.03.2014
comment
О, я думал, что это всего лишь попытка смоделировать то, что было внизу. Если быть более точным. Если вам нужны другие спецификаторы доступа, я не уверен, я должен попробовать. - person iavr; 20.03.2014
comment
У меня только теоретический интерес, не волнуйтесь так сильно. :-) - person Constructor; 20.03.2014
comment
Вы меня застряли, так что смотрите мой новый ответ :-) - person iavr; 20.03.2014

Попробуйте использовать «Специализацию шаблона»:

template <typename T>
struct S {
    void foo();
    void bar();
};

template <>
struct S<int> {
    void foo();
};



int main()
{
    S<char> sc;
    sc.foo();
    sc.bar();

    S<int> si;
    si.foo();
    si.bar(); // compile error: 'bar' : is not a member of 'S<int>'

}   
person qehgt    schedule 20.03.2014
comment
Спасибо, но я хочу использовать специализации членов шаблона класса (и шаблоны членов класса), а не весь шаблон класса. - person Constructor; 20.03.2014