首页 > 解决方案 > 是否可以避免最终不会运行的 c++ 模板函数的部分错误?

问题描述

我有一个带有两个整数作为输入的模板。一个可能比另一个有更大的类型。我的代码相应地进行了转换,因此结果适合目标类型

下面是函数的基本思想:

template<typename S, typename D>
D convert(S a)
{
     return static_cast<D>(a);
}

S但是,当和之间的大小D发生变化时,我想改变值。所以我添加了几个条件:

    if(sizeof(S) < sizeof(D))
    {
        return a << (sizeof(D) - sizeof(S)) * 8;
    }

    if(sizeof(S) > sizeof(D))
    {
        return a >> (sizeof(S) - sizeof(D)) * 8;
    }

问题是我收到这些错误:

Conversions.cpp:在 'void convert(buffer_&) [with S = unsigned char; D = 短无符号整数;buffer_t = std::vector]':
conversions.cpp:从这里需要
conversions.cpp:错误:右移计数 >= 类型的宽度 [-Werror=shift-count-overflow]

  d[idx] = convert_sign<S, D>(static_cast<std::int64_t>(s[idx]) >> (sizeof(S) - sizeof(D)) * 8);

_注意:对于那些不理解的人,错误块中的(sizeof(S) - sizeof(D))or (sizeof(D) - sizeof(S))when将是负数,因此被视为非常大的移位参数(由于移位参数被视为无符号值,它非常大而不是负数,无论如何返回a未签名。) if()sizeof()std::size_t

显然,我可以使用编译指示忽略警告并完成它。

不过,我所期望的是,if()that hasfalse不会被编译,所以不会有错误,因为那发生在编译时(即编译器知道该if()块是否会在它被编译时被执行。)是有没有办法不使用编译指示并仍然避免错误?

标签: c++templatesc++14c++17template-meta-programming

解决方案


但是,我所期望的是,具有 false 的 if() 不会被编译,因此不会有错误,因为那发生在编译时(即编译器知道 if() 块是否将在它被编译的时间。)

不幸的是,您正在描述的行为if constexpr只能从 C++17 开始

当你写

if constexpr ( some_compile_time_test )
   some_code_1;
else
   some_code_2;

wheresome_compile_time_test是可以在编译时确定的测试(as sizeof(S) < sizeof(D)),编译器编译some_code_1- 并完全忽略some_code_2- 当测试是true,反之亦然,否则

如果你只写

if ( some_test )
   some_code_1;
else
   some_code_2;

如果测试some_test是否是可推断的编译时间并不重要:编译器可以优化代码而忽略未使用的部分,但该部分必须是可编译的。

在 C++17 之前(主要但不仅是 C++11 和 C++14),您必须开发两个(或更多)不同的函数/方法。

寻找“SFINAE”和“标签调度”来查看一些有用的方法。

SFINAE 的一个例子

template <typename S, typename D>
typename std::enable_if<(sizeof(S)<sizeof(D)), S>::type convert (S a)
 { return a << (sizeof(D) - sizeof(S)) * 8; }

template <typename S, typename D>
typename std::enable_if<(sizeof(S)>sizeof(D)), S>::type convert (S a)
 { return a >> (sizeof(S) - sizeof(D)) * 8; }

和一个标签调度的例​​子(注意:代码未测试)

template <typename S, typename D>
S convert (S a, std::true_type)
 { return a << (sizeof(D) - sizeof(S)) * 8; }

template <typename S, typename D>
S convert (S a, std::false_type)
 { return a >> (sizeof(S) - sizeof(D)) * 8; }

template <typename S, typename D>
S convert (S a)
 { return convert<S, D>(a, std::integral_constant<bool, (sizeof(S)<sizeof(D))>{}); }

推荐阅读