Можно ли установить разные модификаторы доступа для специализаций шаблонов членов класса? Пример кода (не компилируется):
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);
};
Можно ли установить разные модификаторы доступа для специализаций членов шаблона класса? Пример кода (не компилируется):
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) Я ищу обходной путь C ++ 03 для такого кода C ++ 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;
После некоторого обсуждения, здесь приводится полный отчет обо всех случаях, включая различные спецификаторы доступа, используя подход моего предыдущий ответ.
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
не может иметь аргумент шаблона по умолчанию, потому что это было бы неоднозначно.
Мы можем сделать это с 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!
}
Для конструктора или метода по умолчанию без входных параметров, я думаю, что ответ на Специализация шаблона в определении класса достаточно. Он использует только 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) { ... }
// ...
};
Попробуйте использовать «Шаблон специализации»:
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>'
}