C ++:部分特化的手动消歧(使用SFINAE)

时间:2012-10-27 11:42:03

标签: c++ templates c++11 template-specialization sfinae

我正在实现一个泛型类,它对于不同类型的集合应该表现不同(不仅适用于不同的离散类型)。目标是序列化不同类型的对象以通过自定义协议发送它们(但它是更多的教育任务,而不是实用的东西;我是一个对分布式计算感兴趣的学生)。例如,我需要以不同方式发送浮点数和整数。我还想拥有其他POD类型的默认处理程序。但我需要覆盖一些我的POD类型的行为......

我发现SFINAE方法非常有用,并为使用SFINAE原理和部分特化的自定义类型实现了整数和浮点类型的泛型类(参见下面的代码)。但是,当我试图实现其他POD类型的处理程序,希望其他处理程序将重叠更一般的POD类型处理程序时,我遇到了歧义问题。实际上,对于POD类型及其子集 - 我的GenericObject类的可能特化没有重叠 - 整数和浮点类型。

我试图实现专业化的手动排序,阅读很多关于部分排序的内容,关于如果专业化更专业的话,优先选择专业化。但我未能解决问题。我不知道如何以手动方式消除我的部分专业化。

我不接受排除一组浮点类型和我的POD类型处理程序的整数类型的解决方案,因为这种方式会在处理程序之间产生过多的依赖关系。我希望有一个正确的方法来解决我的问题。例如,在程序开始时,所有静态资源都使用多个优先级进行初始化。在GCC中,我可以使用属性构造函数来控制这种初始化的顺序:__ attribute __((constructor(101)))或类似的属性init_priority。如果我能以这种方式重新排序模板部分特化,我会很高兴。

你能建议我什么吗?

这是我的代码:

#include <type_traits>
#include <iostream>
#include <cxxabi.h>

// General form
template <typename T, typename Enable0 = void>
struct GenericObject {
    char * description() {
        return (char *)"Undefined";
    }
};

// Specialization for integral types
template <typename T>
struct GenericObject<T, typename std::enable_if<std::is_integral<T>::value>::type> {
    char * description() {
        return (char *)"Integral";
    }
};

// Specialization for real types
template <typename T>
struct GenericObject<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
    char * description() {
        return (char *)"Real";
    }
};

// Specialization for other POD types. It MUST be less specialized than specializations for real and integral types, because in other way there will be an ambiguity, because every integral type is also a POD.
/*

    HERE IS MY PROBLEM

*/
template <typename T>
struct GenericObject<T, typename std::enable_if<std::is_pod<T>::value>::type> {
    char * description() {
        return (char *)"POD";
    }
};

// Declaration of types
struct IAmDefined {};
struct IAmUndefinedPOD {};
struct IAmUndefinedComplexClass : virtual IAmUndefinedPOD {};

// Specialization for IAmDefined class and also the most specialized template specialization.
template <>
struct GenericObject<IAmDefined> {
    char * description() {
        return (char *)"Defined";
    }
};

// Produces nice output
std::string demangle(const char *raw) {
    int status;

    char *demangled = abi::__cxa_demangle(raw, 0, 0, &status);
    std::string result(demangled);
    free(demangled);

    return result;
}

template <typename T>
void testObject() {
    GenericObject<T> object;
    std::cout << demangle(typeid(T).name()) << ": " << object.description() << std::endl;
}

int main() {    
    testObject<int>(); // Integral
    testObject<long>(); // Integral
    testObject<float>(); // Real
    testObject<double>(); // Real
    testObject<void>(); // POD
    testObject<IAmDefined>(); // Defined
    testObject<IAmUndefinedPOD>(); // POD
    testObject<IAmUndefinedComplexClass>(); // Undefined
}

以下是编译时错误:

g++ --std=c++11 main.cc -o specialization-of-sets
main.cc: In instantiation of 'void testObject() [with T = int]':
main.cc:85:21:   required from here
main.cc:68:22: error: ambiguous class template instantiation for 'struct GenericObject<int, void>'
main.cc:15:8: error: candidates are: struct GenericObject<T, typename std::enable_if<std::is_integral<_Tp>::value>::type>
main.cc:36:8: error:                 struct GenericObject<T, typename std::enable_if<std::is_pod<_Tp>::value>::type>
main.cc:68:22: error: 'GenericObject<int, void> object' has incomplete type
main.cc: In instantiation of 'void testObject() [with T = long int]':
main.cc:86:22:   required from here
main.cc:68:22: error: ambiguous class template instantiation for 'struct GenericObject<long int, void>'
main.cc:15:8: error: candidates are: struct GenericObject<T, typename std::enable_if<std::is_integral<_Tp>::value>::type>
main.cc:36:8: error:                 struct GenericObject<T, typename std::enable_if<std::is_pod<_Tp>::value>::type>
main.cc:68:22: error: 'GenericObject<long int, void> object' has incomplete type
main.cc: In instantiation of 'void testObject() [with T = float]':
main.cc:87:23:   required from here
main.cc:68:22: error: ambiguous class template instantiation for 'struct GenericObject<float, void>'
main.cc:23:8: error: candidates are: struct GenericObject<T, typename std::enable_if<std::is_floating_point<_Tp>::value>::type>
main.cc:36:8: error:                 struct GenericObject<T, typename std::enable_if<std::is_pod<_Tp>::value>::type>
main.cc:68:22: error: 'GenericObject<float, void> object' has incomplete type
main.cc: In instantiation of 'void testObject() [with T = double]':
main.cc:88:24:   required from here
main.cc:68:22: error: ambiguous class template instantiation for 'struct GenericObject<double, void>'
main.cc:23:8: error: candidates are: struct GenericObject<T, typename std::enable_if<std::is_floating_point<_Tp>::value>::type>
main.cc:36:8: error:                 struct GenericObject<T, typename std::enable_if<std::is_pod<_Tp>::value>::type>
main.cc:68:22: error: 'GenericObject<double, void> object' has incomplete type

我使用GCC 4.8:

gcc版本4.8.0 20120314(实验性)[主干修订版185382](Ubuntu / Linaro 20120314-0ubuntu2)

提前谢谢。

2 个答案:

答案 0 :(得分:7)

我个人会用重载而不是类模板部分特化来解决这个问题。通过这种方式,您可以轻松引入平局:

template<class T> struct type{};

namespace detail{
template<class T> using Invoke = typename T::type;
template<class C, class T = void> using EnableIf = Invoke<std::enable_if<C::value, T>>;
template<class T> using EType = type<EnableIf<T>>;

// we need two tie-breakers here, one for general vs specialized case
// and one for specialized vs more specialized case
template<class T>
char const* describe(EType<std::is_integral<T>>, int, int){ return "Integral"; }
template<class T>
char const* describe(EType<std::is_floating_point<T>>, int, int){ return "Real"; }
template<class T>
char const* describe(EType<std::is_pod<T>>, int, long){ return "POD"; }
template<class T>
char const* describe(type<void>, long, long){ return "Undefined"; }
} // detail::

template<class T>
char const* describe(type<T>){
  // literal '0' is 'int', as such 'int' overloads are preferred
  return detail::describe<T>(type<void>(), 0, 0);
}

// ...

// simple overload for specialized types
char const* describe(type<IAmDefined>){
  return "Defined";
}

Live example.请注意void属于"Undefined"类别,它不是POD。

答案 1 :(得分:0)

我总是用std :: enable_if手动解决歧义,但是当你有很多选择时,这种方法可以成为样板:

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
const char* describe(T,
                     typename enable_if<is_integral<T>::value>::type* = 0)
{
    return "integral";
}

template<typename T>
const char* describe(T,
                     typename std::enable_if<is_floating_point<T>::value>::type* = 0)
{
    return "floating_point";
}

template<typename T>
const char* describe(T,
                     typename std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value && std::is_pod<T>::value>::type* = 0)
{
    return "pod";
}

http://ideone.com/jb70Cd