【问题标题】:Template specialization for a set of types一组类型的模板特化
【发布时间】:2010-12-03 19:11:43
【问题描述】:

如何为一组数据类型专门化一个模板?例如:

template<typename T>
inline T getRatio(T numer, T denom){
    return (numer/denom);
}

我想将它专门用于一组数据类型,因此它仅适用于 intlongdoublefloat。因此,如果用户尝试将此函数与 char 类型一起使用,编译器会抛出错误。

【问题讨论】:

  • 所以你希望它只适用于 long、double、float 和其他类型?

标签: c++ templates


【解决方案1】:

这取决于你想做什么。如果您希望编译器无法为函数调用找到合适的解决方案,您可以使用 Flinsch 的答案,这可能是最好的,或者您可以使用 SFINAE:

template < typename T > is_an_ok_type : boost::mpl::false_ {};
template < > is_an_ok_type<int> : boost::mpl::true_ {};
... etc...

template < typename T >
typename boost::enable_if< is_an_ok_type<T>,T >::type
get_ratio(T t1, T t2)
{
  return t1/t2;
}

如果你想要某种合理可读的错误,你可以使用静态断言; static_assert (C++0x) 或 BOOST_STATIC_ASSERT。

【讨论】:

  • 能否进一步简化此解决方案,将所有“etc”类型放在一个语句中?
【解决方案2】:

由于只有longdoublefloat这三种数据类型是相关的,不需要额外的泛型版本,只需拒绝template并为long、@987654326提供三个函数@和float

【讨论】:

  • 这意味着要维护三个功能,而不仅仅是一个。可能只需要三个和函数的简单性就可以了,但让我感到紧张的是,将来某个维护程序员只会更改其中的一部分。
  • @KeithB -​​ 你知道非模板函数可以调用模板函数,对吧?
  • @Noah - 是的,我只是没有想到使用重载函数转发到模板函数。在我的辩护中,答案的措辞看起来像(无论如何对我来说)建议是编写函数的三个版本。
  • @Flinsch... 如果您只为 long、double 和 float 类型提供重载函数。那么其中一个也将被调用用于 char 类型参数(隐式转换)...所以也就是说,这也不是解决方案。
  • @Nawaz - 好点。我想如果 OP 确实试图限制这些事情,我的版本就会成为正确的答案。
【解决方案3】:

你可以这样做:

// declaration
template <typename T> inline T getRatio(T numer, T denom);

// specialization for long    
template <>
inline long getRatio<long>(long numer, long denom) { return (numer / denom); }
// specialization for float
template <>
inline float getRatio<float>(float numer, float denom) { return (numer, denom); }
// specialization for double
template <>
inline double getRatio<double>(double numer, double denom) { return (numer / denom); }

如果使用 long、float 或 double 以外的类型调用 getRatio,这将导致链接器错误。

【讨论】:

  • 重载或 SFINAE 方法会比这更可取。当编译器无法匹配函数调用时(如重载/sfinae),您通常会获得行位置等...信息,您可以使用它来查找问题。对于链接器错误,您通常最多只能给出试图链接到该函数的目标文件。
【解决方案4】:

如果您想将getRatio() 函数限制为仅用于int, long, double and float,那么您也可以使用此函数。如果您使用 char 类型参数调用它,它将产生“有意义的”编译错误。编译错误将是:this_type_is_not_allowed_in_getRatio

//yourheader.h
template<typename T>
inline T getRatio(T numer, T denom)
{
    typedef typelist<int, typelist<long, typelist<double, float>>> allowedtypes;
    compile_time_checker<contains<allowedtypes, T>::result> this_type_is_not_allowed_in_getRatio;
    return (numer/denom);
}

它使用这个标题:

//metafunctions.h
template<typename H, typename T>
struct typelist
{
    typedef H Head;
    typedef T Tail;
};

template<typename T, typename Tail> 
struct contains
{
    static const bool result = false;
};

template<typename Head, typename Tail, typename T> 
struct contains<typelist<Head, Tail>, T>
{
    static const bool result = false || contains<Tail, T>::result;
};

template<typename T, typename Tail> 
struct contains<typelist<T, Tail>, T>
{
    static const bool result = true || contains<Tail, T>::result;
};

template<bool b> struct compile_time_checker;
template<> struct compile_time_checker<true> {};

希望对您有所帮助。 您现在可以在一个函数中编写所有代码!

【讨论】:

    【解决方案5】:

    语言中没有内置方法来指定模板只能使用特定的类型参数集进行实例化。但是,对于没有定义 operator / 的任何类型,这将无法编译,这对您来说可能已经足够了。

    在设计 API 时,避免让用户感到惊讶是一种很好的做法,如果您告诉他们不允许计算两个可除数的比率,大多数用户会感到惊讶!

    如果您真的不想要默认行为,Flinsch 的回答是一个不错的折衷方案。

    【讨论】:

    • “语言中没有办法指定模板只能用一组特定的类型参数来实例化。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-14
    相关资源
    最近更新 更多