简体   繁体   English

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

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

I have a templated matrix class that I explicitly instantiate for various POD types and custom class types. 我有一个模板化的矩阵类,我明确地为各种POD类型和自定义类类型实例化。 Some of the member functions however don't make sense for a few of such custom types. 然而,一些成员函数对于一些这样的自定义类型没有意义。 For example: 例如:

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

Can I prevent the instantiation of the LoadFile function (which is a member function) for Matrix objects of select types? 我可以阻止选择类型的Matrix对象的LoadFile函数(它是成员函数)的实例化吗? So far I have avoided the issue by making LoadFile a friend function and then explicitly controlling its instantiation. 到目前为止,我通过使LoadFile成为友元函数然后显式控制其实例化来避免这个问题。 But I want to know if I can do this when LoadFile is a member function of Matrix . 但是我想知道当LoadFileMatrix的成员函数时我是否可以这样做。

The first question is whether you really need to control this. 第一个问题是你是否真的需要控制它。 What happens if they call that member function on a matrix that stores My_custom_class ? 如果他们在存储My_custom_class的矩阵上调用该成员函数会发生什么? Can you provide support in your class (or the template) so that the member function will work? 你能在你的班级(或模板)中提供支持,以便会员功能有效吗?

If you really want to inhibit the use of those member functions for some particular type, then you can use specialization to block the particular instantiation: 如果你真的想要禁止某些特定类型的成员函数的使用,那么你可以使用特化来阻止特定的实例化:

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

Or even just add static_assert s to the common implementation verifying the preconditions for what types is it allowed or disallowed? 或者甚至只是将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
   }
};

with std::enable_if , this is the best I can come up with 使用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>()

only type int compile while other don't. 只输入int compile而其他不输入。

Can I prevent the instantiation of the LoadFile function (which is a member function) for Matrix objects of select types? 我可以阻止选择类型的Matrix对象的LoadFile函数(它是成员函数)的实例化吗?

Your best bet here would be to use a static_assert that would create a compiler error when you attempt to call the method in a version of the class instantiated with a blocked type. 这里最好的选择是使用static_assert ,当您尝试在使用阻塞类型实例化的类的版本中调用该方法时,该static_assert会产生编译器错误。 Using std::enable_if , and other methods that would selectively "disable" a method itself would require you to create partial or full specializations of the class with and without the methods in question in order to prevent compiler errors. 使用std::enable_if以及有选择地“禁用”方法本身的其他方法将要求您使用和不使用相关方法创建类的部分或完全特化,以防止编译器错误。 For instance, AFAIK, you cannot do the following: 例如,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(); 
    }
};

If you attempted to instantiate an example<int> , etc., you would end up with a compiler error at the point of instantiation of the object type. 如果您尝试实例化一个example<int>等,则在实例化对象类型时最终会出现编译器错误。 You couldn't simply call example<int>::print() and be okay, and only run into a problem if you chose to call example<int>::another_print() . 你不能简单地调用example<int>::print()并且没关系,只有在你选择调用example<int>::another_print()才会遇到问题。 Specializations of example<T> could get you around the issue, but that can be a bit of a mess. example<T>专业化可以帮助您解决问题,但这可能有点混乱。 As originally surmised, a static_assert would probably be the easiest case to handle, along with a nice message to the end-user explaining what went wrong. 正如最初推测的那样, static_assert可能是最简单的处理方式,并向最终用户提供一条很好的消息,解释出现了什么问题。

Keep in mind that creating compiler errors is the goal, and it's a good one to have. 请记住,创建编译器错误目标,并且它是一个很好的。 If you blocked a method from being instantiated, and the end-user decided to invoke it, you'd end up with a compiler error either way. 如果阻止某个方法被实例化,并且最终用户决定调用它,那么无论哪种方式都会出现编译器错误。 The version without the static_assert will leave a lot of head-scratching as the user of your class attempts to parse a probably very verbose compiler error message, where-as the static_assert method is direct and to the point. 没有static_assert的版本会留下很多令人static_assert ,因为你的类的用户试图解析一个可能非常详细的编译器错误消息,其中static_assert方法是直接的和重点。

If the selected set of types is known at compile time, and you are using c++11 with a compiler that supports type aliases , uniform initialization and constexpr (for example gcc 4.7) you can make your code a bit cleaner like this (from previous example above by yngum): 如果所选的一组类型在编译时是已知的,并且您正在使用带有类型别名的编译器的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>();

Beware of compatibility of this code, though, because these features have been only recently supported and some compilers don't do yet. 但请注意此代码的兼容性,因为这些功能最近才得到支持,而且一些编译器还没有。 You can see more about c++11 compiler support here . 您可以在此处查看有关c ++ 11编译器支持的更多信息。

If you could use the TypeLists from the ( http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315 ) - Loki you could implement something like: 如果你可以使用( 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 >();
  }
};

Then your HasType would be something like: 然后您的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 };
};

In the list you can add the classes which you would like prevent to be passed as the template parameters. 在列表中,您可以添加要阻止作为模板参数传递的类。

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

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