使用SFINAE在C ++中强制进行类型转换

时间:2014-06-26 18:52:03

标签: c++ sfinae

我正在编写一个类(BufferInserter),它透明地将用户定义的消息转换为其网络端格式,并将结果打包到用户提供的缓冲区中。下面是一个Message及其字节交换对应的简单示例:

//Native message (not in network endian format)
struct Message
{
    short val;
    Message(short v): val(v){}
};

//Network endian format of Message
struct OtaMessage
{
    typedef Message NativeType;
    short val;

    operator Message() const
    {
        return Message(val >> 8 | val << 8); 
    }

    OtaMessage(const Message& m)
        : val(val >> 8 | val << 8)
    {}
};

这是BufferInserter的高度简化版本:

class BufferInserter
{
public:
    BufferInserter(char* buffer)
        :buf(buffer)
    {}

    template<typename T>
    char* insertStruct(T s, typename T::NativeType = 0)
    {
        const std::size_t size = sizeof(T);
        *reinterpret_cast<T*>(buf) = s;
        buf += size;
        return buf;
    }
private:
    char* buf;
};

希望是用户可以做类似的事情:

Message m(1);
char buf[256];
BufferInserter ins(buf);
ins.insertStruct(m);

C ++类型推导机制会跳过将本机Message传递给insertStruct,因为Message没有NativeType typedef,而是将Me​​ssage转换为OtaMessage。这不是发生的事情,而是我得到编译器错误(g ++ 4.7)

test.cpp:55:23: error: no matching function for call to ‘BufferInserter::insertStruct(Message&)’
     ins.insertStruct(m);
                       ^
test.cpp:55:23: note: candidate is:
test.cpp:34:11: note: template<class T> char* BufferInserter::insertStruct(T, typename T::NativeType)
     char* insertStruct(T s, typename T::NativeType = 0)
           ^
test.cpp:34:11: note:   template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<class T> char* BufferInserter::insertStruct(T, typename T::NativeType) [with T = Message]’:
test.cpp:55:23:   required from here
test.cpp:34:11: error: no type named ‘NativeType’ in ‘struct Message’

MSVC 2013也有类似的错误,因此它可能不是编译器错误。

显然这有效:

Message m(1);
char buf[256];
BufferInserter ins(buf);
ins.insertStruct(OtaMessage(m));

但我想避免用户必须知道字节交换。我还可以在Message结构中向OtaMessage添加转换运算符:

struct Message
{
    short val;
    Message(short v): val(v){}
    operator OtaMessage()
    {
       val = v<<8 | v>>8;
    }
};

这有两个问题:

  1. 同样,我不希望用户知道OtaMessage
  2. OtaMessage在Message之后定义,因此消息 - &gt; OtaMessage是不可能的。有趣的是,在MSVC 2013中尝试这个之后,我崩溃了编译器:
  3.   

    1&gt; Source.cpp(74):致命错误C1001:编译器中发生内部错误。   1 GT; (编译器文件'msc1.cpp',第1325行)   1 GT;要解决此问题,请尝试简化或更改上面列出的位置附近的程序。

    任何帮助?

2 个答案:

答案 0 :(得分:2)

这不是类型推断的工作原理。也许你可以实现类似特征的方法,让BufferInserter::insertStruct<Message>推导出来,然后用你的特质类转换。它的一些关键部分可能如下所示:

template <typename T>
struct OtaConverter {
  // using ota_type = T; -- don't provide a base ota_type
};

: : :

template <> struct OtaConverter<Message> {
  using ota_type = OtaMessage;
};

: : :

template<typename T>
char* BufferInserter::insertStruct(T s, typename OtaConverter<T>::ota_type* = 0)
{
    using OT = typename OtaConverter<T>::ota_type;
    const std::size_t size = sizeof(OT);
    *reinterpret_cast<OT*>(buf) = OT(s);
    buf += size;
    return buf;
}

您还需要修复OtaMessage构造函数,因为它当前未引用m.val。为了安全起见,您还应该明确标记构造函数。

live example。请注意,删除OtaConverter的特化会导致故意编译错误。

答案 1 :(得分:0)

我遇到了同样的问题(在其他情况下)并通过更新我的MSVC2013工具修复了它 - &gt;扩展程序和更新 - &gt;更新 - &gt;产品更新(自更新2 https://support.microsoft.com/fr-fr/kb/2927432以来修复了错误)。

干杯,