c++ - 类型特征以获得默认参数提升

标签 c++ typetraits variadic-functions

[免责声明:我知道这个问题的答案。我认为这可能会引起一些普遍兴趣。]

问题:我们如何才能拥有一个类型特征来生成执行默认参数提升所产生的类型?

动机:我希望能够方便地使用变量参数。例如:

void foo(char const * fmt, ...);  // Please pass: * unsigned short
                                  //              * bool
                                  //              * char32_t
                                  //              * unsigned char

当将参数传递给不带参数的函数调用时,即匹配省略号,参数会进行默认参数提升。到目前为止一切顺利,但这些促销事件取决于平台。我可以使用 va_arg(ap, T) 恢复参数,但是 T 是什么?

现在,对于一些简单的情况,这很容易:例如,我总是可以说:

unsigned short n = va_args(ap, unsigned int);

默认提升将导致 signed intunsigned int,但是根据 C11 7.16.1.1/3,va 转换为 unsigned int 总是没问题,因为即使默认提升导致 int,原始值也可以用两种类型表示。

但是当我期望 char32_t 时我应该转换成什么类型​​? C++11 4.5/2 使结果类型完全开放。所以我想要一个让我写的特征:

char32_t c = va_args(ap, default_promote<char32_t>::type);

如何做到这一点?

当参数类型不能作为变量参数传递时产生静态断言的特征的加分点。

最佳答案

这是适用于“大多数”类型(整型、浮点型、无作用域枚举、数组、指针、指向成员的指针、函数、函数指针)的解决方案框架。

#include <type_traits>

template <typename U>
struct default_promote
{
    // Support trait for scoped enums

    template <typename E, bool IsEnum>
    struct is_unscoped_enum : std::false_type { };

    template <typename E> struct is_unscoped_enum<E, true>
    : std::is_convertible<E, typename std::underlying_type<E>::type> { };


    // Floating point promotion

    static double test(float);


    // Integral promotions (includes pointers, arrays and functions)

    template <typename T, typename = typename std::enable_if<!is_unscoped_enum<T, std::is_enum<T>::value>::value>::type>
    static auto test(T) -> decltype(+ std::declval<T>());

    template <typename T, typename = typename std::enable_if<is_unscoped_enum<T, std::is_enum<T>::value>::value>::type>
    static auto test(T) -> decltype(+ std::declval<typename std::underlying_type<T>::type>());


    // Pointers-to-member (no promotion)

    template <typename T, typename S>
    static auto test(S T::*) -> S T::*;


    using type = decltype(test(std::declval<U>()));
};

它不为无法通过省略号安全传递的类型提供诊断。此外,此解决方案涵盖了类型在作为可变函数参数传递时所经历的衰减,因此它不仅仅与提升有关。

它通过显式处理指向成员的指针类型和 float 转换,并依靠一元运算符 + 来处理整型和无作用域的枚举类型;例如C++11 5.3.1/7:

The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument. Integral promotion is performed on integral or enumeration operands. The type of the result is the type of the promoted operand.

需要做一些额外的工作来处理枚举,因为可以为枚举重载运算符(作用域和非作用域),因此必须小心使用朴素的一元加运算符。也就是说,当枚举未限定范围时,我们必须考虑提升底层类型,并完全禁止限定范围的枚举。

关于c++ - 类型特征以获得默认参数提升,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20442347/

相关文章:

c++ - 防止用户离开 std::experimental::filesystem 中的目录

c++ - C1001 : An internal error has occurred in the compiler

c++ - 如何实现可变参数模式以将可变数量的参数转发给 C++11 中的函数?

c++ - 可变字符串比较

c++ - 试图在 C++ 中对一个简单的字符串进行排序

c++ - 如何返回 const std::vector<Object *const>?

c++ - 如何使用顶点的测地线距离来平滑骨骼顶点权重?

c++ - 可变参数模板和类型特征

c++ - 没有编译器 Hook ,哪些<type_traits>无法实现?

c++ - 在 C++ 中将变量参数列表序列化为字节数组的最佳方法?