如何处理空参数包的情况

4
我有以下代码,用于获取包中所有基本类型的大小(我以特殊方式处理浮点数和双精度浮点数),但当包为空时,我的程序无法编译。
// terminating general case for T being anything else
template <class T>
size_t getSize()
{
    return sizeof(T);
}

// terminating special case for T=double
template<>
size_t getSize<double>()
{
    return SIZEOF_DOUBLE;
}

// terminating special case for T=float
template<>
size_t getSize<float>()
{
    return SIZEOF_FLOAT;
}

// recursive case
template <class T, class U, class ...S>
size_t getSize()
{
    return getSize<T>() + getSize<U, S...>();
}

我希望getSize在这种情况下返回0:

template <class ...T>
void foo(T... arg)
{
    size_t sizeOfTypes = getSize<T...>();
}

T={}时,即以foo<>();的形式调用foo

请注意,我不想修改foo的调用方式,尖括号必须保留。最好是修改getSize,因为我在除了foo之外的几个函数中使用它。将foo修改为最后一步。

3个回答

3
首先是一个函数模板,它将单个类型转换为其大小,可能会有特化:
template <class T>
constexpr size_t getSize_single()
{
    return sizeof(T);
}

template<>
constexpr size_t getSize_single<double>()
{
    return SIZEOF_DOUBLE;
}

template<>
constexpr size_t getSize_single<float>()
{
    return SIZEOF_FLOAT;
}

接下来,有一个东西可以将类型包扩展为大小的std::initializer_list<size_t>,然后将它们加起来:
template <class... Ts>
constexpr size_t getSize() // constexpr for C++14 only - remove for C++11
{
    std::initializer_list<size_t> l{getSize_single<Ts>()...};
    size_t sum = 0;
    for(auto s : l) sum += s;
    return sum;
}
< p > < code >initializer_list< /code >的显式类型是需要处理空包情况的。< /p >

你甚至可以使用 std::accumulate - Jarod42
模板递归的美已经消失了,是吗?实际上,除了解决我遇到的问题之外,使用初始化列表进行迭代是否有任何好处? - Flying Hat
@FlyingHat 我认为这样更加清晰易懂,对编译器也更加友好,因为需要实例化的模板更少。 - T.C.
@Jarod42 accumulate不是constexpr。然而,通过仅添加关键字即可使上述内容在C++14中成为constexpr - T.C.

3

解决此问题的另一种方法是使用一对小结构体,例如:

template<typename T>
struct GetTypeSize
{
    enum { value = sizeof(T) };
};

template<>
struct GetTypeSize<float>
{
    enum { value = SIZEOF_FLOAT };
};

template<>
struct GetTypeSize<double>
{
    enum { value = SIZEOF_DOUBLE };
};

template<typename...>
struct GetSize 
{
    enum { value = 0 };
};

template<typename Head, typename... Tail>
struct GetSize<Head, Tail...>
{
    enum { value = GetTypeSize<Head>::value + GetSize<Tail...>::value };
};

template<typename... T>
void foo(T... arg)
{
    size_t sizeOfTypes = GetSize<T...>::value;
}

这样做的优点是在编译时进行求值(求和)。

我使用了两种类型的结构体。一种用于递归(GetSize),另一种用于获取类型的实际大小(GetTypeSize)。只要存在头部(包不为空),GetSize<Head, Tail...>的特化就会被实例化,并将Head类型的大小添加到GetSize<Tail...>的递归调用中。一旦没有Head,就会使用回退GetSize模板。

对于GetSize<int, double, char>的实例化,结果为

GetTypeSize<int>::value + GetTypeSize<double>::value + GetTypeSize<char>::value + GetSize<>::value

然后是

sizeof(int) + SIZEOF_DOUBLE + sizeof(char) + 0


2

如果您只想编辑getSize函数,则将第一个函数的模板T初始化为void,并添加一个额外的模板函数,以返回0的void类型。它将看起来像这样:

template <class T=void> ////A little edit here
size_t getSize()
{
    return sizeof(T);
}

// terminating special case for T=double
template<>
size_t getSize<double>()
{
    return SIZEOF_DOUBLE;
}

// terminating special case for T=float
template<>
size_t getSize<float>()
{
    return SIZEOF_FLOAT;
}
/////Extra entry for void type
template<>
size_t getSize<void>()
{
    return 0;
}

当调用 foo<>() 时,默认情况下 T 将被设置为 void,因此将调用此最后一个函数而不是其他函数,并且它将返回大小为 0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接