繁体   English   中英

防止模板化成员函数被实例化为给定类型

[英]Prevent templated member function from being instantiated for a given type

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

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

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

第一个问题是你是否真的需要控制它。 如果他们在存储My_custom_class的矩阵上调用该成员函数会发生什么? 你能在你的班级(或模板)中提供支持,以便会员功能有效吗?

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

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

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

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

使用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 compile而其他不输入。

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

这里最好的选择是使用static_assert ,当您尝试在使用阻塞类型实例化的类的版本中调用该方法时,该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<int>等,则在实例化对象类型时最终会出现编译器错误。 你不能简单地调用example<int>::print()并且没关系,只有在你选择调用example<int>::another_print()才会遇到问题。 example<T>专业化可以帮助您解决问题,但这可能有点混乱。 正如最初推测的那样, static_assert可能是最简单的处理方式,并向最终用户提供一条很好的消息,解释出现了什么问题。

请记住,创建编译器错误目标,并且它是一个很好的。 如果阻止某个方法被实例化,并且最终用户决定调用它,那么无论哪种方式都会出现编译器错误。 没有static_assert的版本会留下很多令人static_assert ,因为你的类的用户试图解析一个可能非常详细的编译器错误消息,其中static_assert方法是直接的和重点。

如果所选的一组类型在编译时是已知的,并且您正在使用带有类型别名的编译器的c ++ 11, 统一初始化constexpr (例如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编译器支持的更多信息。

如果你可以使用( 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 };
};

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM