是否有可能避免最终不会运行的c ++模板函数部分中的错误?

时间:2019-05-07 19:38:19

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

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

以下是该功能的基本概念:

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

但是,当SD之间的大小改变时,我想移动该值。因此,我添加了一些条件:

    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_&)[具有S =无符号字符; D =短无符号int; 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))(sizeof(D) - sizeof(S))在错误范围内。 >,因此它实际上与shift参数一样大(因为将shift参数视为无符号值,所以它非常大且不是负数,无论如何if()返回一个无符号的sizeof()。)

很明显,我可以使用编译指示来忽略该警告并进行处理。

不过,我期望的是具有std::size_t的{​​{1}}不会被编译,因此不会出现错误,因为这是在编译时发生的(即编译器知道{{ 1}}块将在编译时执行还是不执行。)是否有一种方法可以不使用该编译指示并仍然避免该错误?

1 个答案:

答案 0 :(得分:4)

  

不过,我期望的是具有false的if()不会被编译,因此不会出现任何错误,因为这是在编译时发生的(即,编译器知道if()块是否将被执行或而不是在它编译的时候。)

您正在描述if constexpr的行为,很不幸,该行为仅从C ++ 17开始可用

写作时

if constexpr ( some_compile_time_test )
   some_code_1;
else
   some_code_2;

其中some_compile_time_test是可以决定编译时的测试(如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))>{}); }