【问题标题】:enable_if in template Parameters Creates Template Redefinition Error模板参数中的 enable_if 创建模板重定义错误
【发布时间】:2015-02-23 13:07:08
【问题描述】:

this answer 中,我真正想做的是在我的模板参数中定义一个typename,它可以用于强制转换返回。

所以这个:

template <typename T>
typename std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type caster(T value){ return reinterpret_cast<unsigned char&>(value); }

会变成这样:

template <typename T, typename R = std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type >
R caster(T value){ return reinterpret_cast<R&>(value); }

对于单个模板特化,这可以正常工作和表现,但假设我添加了另一个特化:

template <typename T, typename R = std::enable_if<sizeof(short) == sizeof(T), short>::type>
R caster(T value){ return reinterpret_cast<R&>(value); }

现在我得到一个错误:

错误 C2995: 'R caster(T)' : 函数模板已定义

有没有办法让编译器相信,对于任何给定的调用,只有其中一个专门化会真正构建?

【问题讨论】:

    标签: c++ templates typename redefinition enable-if


    【解决方案1】:

    不,没有。模板默认参数就是这样,defaults。任何用户都可以调用caster&lt;short, short&gt;,这将匹配两个重载。

    但是,可以添加更多的虚拟参数。

    template <typename T,
              typename R = typename std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type >
    R caster(T value) { return reinterpret_cast<R&>(value); }
    
    template <typename T,
              typename R = typename std::enable_if<sizeof(short) == sizeof(T), short>::type,
              typename = void>
    R caster(T value) { return reinterpret_cast<R&>(value); }
    

    (还要注意添加的typename。)


    但是,由于所有主体都是相同的,我可能不会使用重载。

    template <std::size_t N>
    struct cast_result;
    
    template <>
    struct cast_result<sizeof(std::uint8_t)> {
      typedef std::uint8_t type;
    };
    
    template <>
    struct cast_result<sizeof(std::uint16_t)> {
      typedef std::uint16_t type;
    };
    
    ...
    
    template <typename T, typename R = typename cast_result<sizeof(T)>::type>
    R caster(T value) {
      return reinterpret_cast<R&>(value);
    }
    

    最后一点:reinterpret_cast 的这种使用违反了别名规则。但是,这很容易解决:

    template <typename T, typename R = typename cast_result<sizeof(T)>::type>
    R caster(T value) {
      R result;
      std::memcpy(&result, &value, sizeof result);
      return result;
    }
    

    【讨论】:

    • 啊,至少我明白为什么它现在没有构建。似乎我可以设置一个typedef 或者不是用户参数的东西,但会定义使用的类型。
    • 我在reinterpret_cast 上没有看到任何错误?对我来说,它应该按原样工作当然是有道理的。如果位数相同,则将一个视为另一个应该没有问题?
    • @JonathanMee 语言本可以这样定义,但事实并非如此。该标准包含:“如果程序试图通过以下类型之一的左值访问对象的存储值,则行为未定义:”后跟一个列表,其中不包含任何关于特定大小对象的注释.一些编译器(如 GCC)使用该规则来积极优化代码,并指出例如给定int *along *b,存储到*a 并从*b 读取的代码,编译器可以重新排序访问,因为指针不允许指向同一个对象。
    • @JonathanMee 如果你使用gcc -O2 -Wall 编译,你会收到一个警告:“取消引用类型双关指针会破坏严格的别名规则”。 (注意:该警告被简单地抑制了,但不要误以为导致该警告的代码没有别名违规。)
    【解决方案2】:

    似乎这里最好的解决方案可能是使用大量的conditionals,这样可以避免我不得不用模板特化来愚弄:

    template <typename T, typename R = std::conditional<sizeof(T) == sizeof(unsigned char),
                                                        unsigned char,
                                                        conditional<sizeof(T) == sizeof(unsigned short),
                                                                    unsigned short,
                                                                    conditional<sizeof(T) == sizeof(unsigned long),
                                                                                unsigned long,
                                                                                enable_if<sizeof(T) == sizeof(unsigned long long), unsigned long long>::type>::type>::type>::type>
    R caster(T value){ return reinterpret_cast<R&>(value); }
    

    我向读者道歉,因为这就像阅读嵌套的三元组一样。但是,我目前不知道有一种更清洁的方法来处理这个问题。

    遗憾的是,这仍然不能阻止用户通过提供hvd 提到的他自己的第二个模板参数来践踏我的所有默认设置。

    编辑:

    我问了另一个问题here,它有一个解决方案,不需要将typename 放在模板定义中并且不需要两次声明类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-11
      相关资源
      最近更新 更多