【问题标题】:Is it possible to set different access modifiers to a class member template specializations (and a class template member ones)?是否可以为类成员模板特化(和类模板成员特化)设置不同的访问修饰符?
【发布时间】:2014-04-27 13:18:50
【问题描述】:
  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) 我正在为此类 C++11 代码寻找 C++03 解决方法:

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++11 代码寻找 C++03 解决方法:

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;

【问题讨论】:

    标签: c++ templates c++11 template-specialization access-modifiers


    【解决方案1】:

    经过一些讨论,这里是所有案例的完整说明,包括使用我的previous answer 方法的不同访问说明符。

    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:我们怎么可能为默认构造函数显式指定模板参数(因为它无法推断)?

    • 再次查看代码,我认为ClassTemplateSimpleClass 没有太大区别(或根本没有区别)。 ClassTemplate1 不能有默认模板参数,因为那样会模棱两可。

    【讨论】:

    • 这整个问题有点像 XY 问题,但是 +1 解决它!
    • @SamCristall 哦,我不知道这个词,但现在我知道了:-)
    • @SamCristall 是的,我认为您对 XY 问题的看法是正确的。 :-)
    • SimpleClass1 不能用,你是对的。这就是为什么我在我的问题中使用带有一个参数的构造函数。
    【解决方案2】:

    我们可以使用 SFINAE 做到这一点,但不幸的是我们需要 is_sameenable_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(),我们可以再次应用dummy参数,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!
    }
    

    【讨论】:

    • 还有没有输入参数的情况。
    • @iavr 我不关注——你能说得更具体点吗?
    • 如果构造函数(默认构造函数)或方法没有输入参数,则括号内不能有enable_if(或任何东西)。问题指的是没有参数的模板方法的情况(其中模板参数需要由调用者显式指定)。
    • @iavr 我有这种情况,只是不在我的测试中,我会添加它。
    • 另见my answerprevious one
    【解决方案3】:

    对于没有输入参数的默认构造函数或方法,我认为Template specialization within class definition 的答案就足够了。它只使用了std::enable_ifstd::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) { ... }
        // ...
    };
    

    【讨论】:

    • 谢谢。一句话:你在Method之前忘记了void。据我了解,这解决了 1),但不是 2)。我对吗?那么解决一般问题呢?
    • 谢谢,已更正。我认为您混淆了问题,因为(1)和(2)中的每一个都包含两个示例,它们指的是不同的问题。我想我很清楚这里指的是一个(或多个)参数,而我的previous answer 指的是无参数。我错过了什么吗?
    • 我的意思是类模板的问题,而不是类成员模板的问题。
    • 哦,现在我明白了。在 (2) 中,您有一个类模板。嗯,我之前的回答就是这样,只是没有参数。我认为template &lt;only_if &lt;!eq &lt;T, int&gt;{}, int&gt; = 0&gt; SimpleClass(T) { ... }; 应该没问题。否则,添加一个虚拟参数:template &lt;typename D = T, only_if &lt;!eq &lt;D, int&gt;{}, int&gt; = 0&gt; SimpleClass(T) { ... };
    • 好。第二种解决方案更好,因为它允许使用显式实例化:template class SimpleClass&lt;int&gt;;。我可以问一个愚蠢的问题吗?为什么要在方法定义后加分号?
    【解决方案4】:

    尝试使用“模板专业化”:

    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>'
    
    }   
    

    【讨论】:

    • 谢谢,但我想使用类模板成员(和类成员模板)的特化,而不是整个类模板。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多