【问题标题】:Prevent templated member function from being instantiated for a given type防止为给定类型实例化模板化成员函数
【发布时间】:2013-03-26 01:23:42
【问题描述】:

我有一个模板化的矩阵类,我为各种 POD 类型和自定义类类型显式实例化。然而,一些成员函数对一些这样的自定义类型没有意义。例如:

Matrix<int> LoadFile(....); // This makes sense
Matrix<My_custom_class> LoadFile(...); //This doesn't make sense in the context of the custom class

我可以阻止 LoadFile 函数(它是一个成员函数)为选择类型的 Matrix 对象实例化吗?到目前为止,我通过将LoadFile 设为友元函数然后显式控制其实例化来避免该问题。但是我想知道当LoadFileMatrix 的成员函数时我是否可以这样做。

【问题讨论】:

  • @JBentley 这个问题似乎是相关的,但我不太想要同样的结果。我不希望编译器错误阻止我实例化特定变体。我完全想要Matrix&lt;My_custom_class&gt;,但我只是不想要成员函数LoadFile。我正在实例化这些符号并将它们打包在一个库中。因此,如果我在应用程序中对 My_custom_class 类型的矩阵调用 LoadFile,那么我预计会收到一个链接器错误,指出未找到此类符号。
  • 问题从生成链接器错误而不是编译器错误的代码开始,只声明(而不是定义)特化。

标签: c++ templates c++11 metaprogramming


【解决方案1】:

第一个问题是你是否真的需要控制它。如果他们在存储My_custom_class 的矩阵上调用该成员函数会发生什么?您能否在您的类(或模板)中提供支持以使成员函数正常工作?

如果你真的想禁止对某些特定类型使用这些成员函数,那么你可以使用专门化来阻止特定的实例化:

template <typename T>
struct test {
   void foo() {}
};
template <>
inline void test<int>::foo() = delete;

或者甚至只是将static_asserts 添加到通用实现中,以验证允许或禁止哪些类型的先决条件?

template <typename T>
struct test {
   void foo() {
       static_assert(std::is_same<T,int>::value || std::is_same<T,double>::value,
                     "Only allowed for int and double");
       // regular code
   }
};

【讨论】:

  • David,我可以为我的自定义类定义 LoadFile,但是由于该类定义的内部结构相当复杂,我想我或其他任何人都很难将它存储到文件中。
  • 不会对特定函数使用 static_assert 来阻止整个类的实例化吗?我想我将无法创建一个包含来自Matrix&lt;My_custom_class&gt; 的其他符号、构造函数和运算符的库。我的理解正确吗?
  • @deepak:不确定是不是这样,但我不这么认为。成员函数仅在使用时才被实例化。无法实例化的模板是非法的(尽管并非所有编译器都能捕捉到这一点),但这里不是这种情况,因为有可能会编译的实例化。极端情况是第一个示例中的特化,这可能确实会导致编译错误......另一种方法是声明但不定义特化,它将编译但无法链接,不如编译器错误那么好,但仍然是编译时
  • @GManNickG:是的,我认为在 deepak 的评论之后,它不仅不依赖于任何模板参数,而且根本不在模板函数内部(特化不再是模板)
  • static_assert(false,"operation not supported for ints"); 这会使程序格式错误。这不是一个好主意。你可能想在那里有一个= delete;
【解决方案2】:

std::enable_if,这是我能想到的最好的了

template< typename T >
struct Matrix {
    template< typename T >
    Matrix< typename std::enable_if<std::is_integral<T>::value, T>::type >
        LoadFile() 
    {
        return Matrix<T>();
    }
};

Matrix<int> a;
Matrix<int> b = a.LoadFile<int>()

只输入int 编译,而其他不编译。

【讨论】:

    【解决方案3】:

    我可以防止为选择类型的 Matrix 对象实例化 LoadFile 函数(它是一个成员函数)吗?

    您最好的选择是使用static_assert,当您尝试在使用阻塞类型实例化的类版本中调用该方法时,这会产生编译器错误。使用std::enable_if 和其他可以选择性地“禁用”方法本身的方法将要求您创建带有和不带有相关方法的类的部分或全部特化,以防止编译器错误。例如,AFAIK,您不能执行以下操作:

    template <typename T>
    struct test
    {
        static const bool value = false;
    };
    
    template<>
    struct test<double>
    {
        static const bool value = true;
    };
    
    
    template<typename T>
    struct example
    {
        void print() { cout << "Printing value from print()" << endl; }
    
        typename enable_if<test<T>::value, T>::type another_print() 
        { 
            cout << "Printing value from another_print()" << endl;
            return T(); 
        }
    };
    

    如果您尝试实例化 example&lt;int&gt; 等,您将在对象类型实例化时出现编译器错误。您不能简单地拨打example&lt;int&gt;::print() 就可以了,只有当您选择拨打example&lt;int&gt;::another_print() 时才会遇到问题。 example&lt;T&gt; 的专业化可以帮助您解决这个问题,但这可能有点混乱。正如最初推测的那样,static_assert 可能是最容易处理的情况,同时还会向最终用户发送一条很好的消息来解释问题所在。

    请记住,创建编译器错误的目标,这是一个很好的目标。如果您阻止了一个方法被实例化,并且最终用户决定调用它,那么无论哪种方式,您最终都会遇到编译器错误。没有static_assert 的版本会留下很多令人头疼的问题,因为您的类的用户试图解析一个可能非常冗长的编译器错误消息,其中static_assert 方法是直接且切中要害的。

    【讨论】:

      【解决方案4】:

      如果选定的类型集在编译时已知,并且您正在使用 c++11 和支持 type aliasesuniform initializationconstexpr 的编译器(例如 gcc 4.7),您可以使您的代码像这样更干净一些(来自 yngum 上面的示例):

      template <bool Cond, class T = void>
      using enable_if_t = typename std::enable_if<Cond, T>::type;
      
          template< typename T >
          struct Matrix {
      
          template< typename T >
          //std::is_integral has constexpr operator value_type() in c++11. This will work thanks to uniform init + constexpr. With the alias, no more need for typename + ::type
          Matrix<enable_if_t<std::is_integral<T>{}>>
          LoadFile() 
          {
              return Matrix<T>();
          }
      };
      
      Matrix<int> a;
      Matrix<int> b = a.LoadFile<int>();
      

      不过,请注意此代码的兼容性,因为这些功能最近才得到支持,有些编译器还不支持。您可以查看更多关于 c++11 编译器支持的信息here

      【讨论】:

        【解决方案5】:

        如果您可以使用 (http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315) 中的 TypeLists - Loki,您可以实现类似:

        template<bool>
        struct Static_Assert;
        
        template<>
        struct Static_Assert<true>{};
        
        class B{};
        
        template<typename T>
        class A{
        public:
          A(){
            Static_Assert< 0 == utils::HasType<T, TYPELIST_2(B,int) >::value >();
          }
        };
        

        那么你的 HasType 会是这样的:

        template<typename T, typename TList>
        struct HasType{
          enum { value = 0+HasType< T, typename TList::Tail >::value };
        };
        
        template<typename T>
        struct HasType< T, NullType >{
          enum { value = 0 };
        };
        
        template<typename T, typename U>
        struct HasType< T, TypeList<T, U> >{
          enum { value = 1 };
        };
        

        在列表中,您可以添加您希望阻止作为模板参数传递的类。

        【讨论】:

          猜你喜欢
          • 2019-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多