C ++:手动消除部分专业化(使用SFINAE) [英] C++: Manual disambiguation of partial specialization (with SFINAE)

查看:229
本文介绍了C ++:手动消除部分专业化(使用SFINAE)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am implementing a generic class, which should behave differently for different sets of types (not only for different discrete types). The goal is to serialize objects of different types to send them over custom protocol (but it is more educational task rather than something pragmatical; I am a student that is interested in distributed computing). For example, I need to send floats and integers differently. I also want to have a default handler of other POD types. But I need to override the behavior for some my POD types...

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

I have found that SFINAE method very useful and implement generic classes for integral and floating point types, for my custom types using SFINAE principle and partial specialization (see code below). But when I have tried to implement a handler of other POD types hoping that other handlers will overlap more general POD-types handler, I faced with an ambiguity issue. Actually there is no overlapping of possible specializations of my GenericObject class for POD types and a subsets of it - integral and floating point types.

我试图实现手动排序的专业,阅读了很多关于部分排序,关于那一个专业化是优于另一个如果它更专业化。但我没有解决这个问题。我不知道如何以手动方式消除我的部分专业化。

I was trying to implement a manual ordering of specializations, read much about partial ordering, about that one specialization is preferred than another if it is more specialized. But I have failed to solve the problem. I have no ideas how to disambiguate my partial specializations in a manual manner.

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

The solution with excluding a set of floating point types and integral types of my POD-types handler is not acceptable for me, because this way produces an excess dependencies between handlers. I hope there is a right way to resolve my problem. For example, at the start of the program all static resources are initialized with several priorities. In GCC I can control the sequence of such initialization with an attribute constructor: __ attribute__((constructor(101))) or a similar attribute init_priority. I would be pleased if I can reorder template partial specialization in a such way.

你能给我建议吗?

这是我的代码:

#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:

I use GCC 4.8:

gcc version 4.8.0 20120314(experimental)[trunk revision 185382](Ubuntu / Linaro 20120314-0ubuntu2)

gcc version 4.8.0 20120314 (experimental) [trunk revision 185382] (Ubuntu/Linaro 20120314-0ubuntu2)

提前感谢。

推荐答案

我个人会解决这个与重载而不是类模板部分专业化。这样,你可以轻松地引入一个决胜:

I personally would solve this with overloading rather than class template partial specializations. This way, you can easily introduce a tie-breaker:

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 属于未定义类别, 。

Live example. Note that void belongs to the "Undefined" category, it's not a POD.

这篇关于C ++:手动消除部分专业化(使用SFINAE)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆