4

我有一个模板化的矩阵类,我为各种 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.

4

5 回答 5

4

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

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

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

或者甚至只是static_assert在通用实现中添加 s 来验证允许或禁止哪些类型的先决条件?

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
   }
};
于 2013-03-26T01:49:52.650 回答
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>()

只输入intcompile 而其他不输入。

于 2013-03-26T02:03:08.017 回答
1

如果选定的类型集在编译时已知,并且您使用 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 编译器支持的更多信息。

于 2013-03-26T12:10:19.677 回答
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>可以让你解决这个问题,但这可能有点混乱。正如最初推测的那样,astatic_assert可能是最容易处理的情况,同时还会向最终用户发送一条很好的消息来解释问题所在。

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

于 2013-03-26T01:44:48.027 回答
0

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

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

于 2013-03-31T18:40:18.507 回答