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

时间:2013-03-26 01:23:43

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

我有一个模板化的矩阵类,我为各种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的成员函数时,我想知道我是否可以这样做。

5 个答案:

答案 0 :(得分:4)

第一个问题是你是否真的需要控制它。如果他们在存储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
   }
};

答案 1 :(得分: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编译而其他编译不编译。

答案 2 :(得分:1)

  

我可以阻止选择类型的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<int>等,则在实例化对象类型时最终会出现编译器错误。您不能简单地拨打example<int>::print()并保持正常,只有在您选择拨打example<int>::another_print()时才会遇到问题。 example<T>的专业化可以帮助您解决问题,但这可能有点混乱。正如最初推测的那样,static_assert可能是最容易处理的案例,并向最终用户解释错误信息。

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

答案 3 :(得分:1)

如果在编译时已知选定的一组类型,并且您正在使用带有type aliasesuniform initializationconstexpr的编译器的c ++ 11(例如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的更多信息。

答案 4 :(得分:0)

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

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

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