如何编写constexpr函数来操作转发的参考元组?

use*_*188 5 c++ c++17

我编写了一个constexpr函数,用于计算元组元素大小的总和.

直接调用时,函数调用将使用值元组和引用元组进行编译.

当通过模板化函数调用时,它仍然使用值元组编译,但是使用引用元组失败.

我可以使用指针元组而不是参考元组来解决我的问题,但我写的东西的API(一组模板化的功能,以便于为微控制器编写SPI和I²C驱动程序)将不那么干净.

谢谢你的帮助.

Ps:我正在使用gcc8.2使用c ++ 17标准.

亚历山大

#include <tuple>

template <typename> struct is_tuple_t: std::false_type {};
template <typename ...T> struct is_tuple_t<std::tuple<T...>> : std::true_type {};

template<typename Type>
constexpr bool is_tuple(const Type&) {
    if constexpr (is_tuple_t<Type>::value) 
        return true;
    else
        return false;
}

template<class F, class...Ts, std::size_t...Is>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func,
             std::index_sequence<Is...>){
    using expander = int[];
    (void)expander { 0, ((void)func(std::get<Is>(tupl)), 0)... };
}

template<class F, class...Ts>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func){
    for_each_in_tuple(tupl, func, std::make_index_sequence<sizeof...(Ts)>());
}

template <typename T>
constexpr size_t size_of_tuple(const T &tup) {
    static_assert(is_tuple(tup) == true, "error size_of_tuple argument must be a tuple");
    size_t s=0;
    for_each_in_tuple(tup, [&s](auto &&x) {
        s += sizeof(x);
    });
    return s;
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
    constexpr size_t st = size_of_tuple(tup);
    return st;
}

int main()
{
    uint16_t var;

    constexpr size_t s1 = size_of_tuple(std::make_tuple(1)) ;          // OK
    constexpr size_t s2 = size_of_tuple(std::forward_as_tuple(var)) ;  // OK

    constexpr size_t f1 = foo(std::make_tuple(1)) ;          // OK
    constexpr size_t f2 = foo(std::forward_as_tuple(var)) ;  // FAIL
}
Run Code Online (Sandbox Code Playgroud)

Okt*_*ist 1

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  constexpr size_t st = size_of_tuple(tup);
  return st;
}
Run Code Online (Sandbox Code Playgroud)

在此函数中,tup不是常量表达式,因此不能在constexpr变量的初始值设定项中使用。

[expr.const]¶2

表达式e核心常量表达式,除非 [...] 的计算e将计算以下表达式之一:

  • [...]
  • 引用引用类型的变量或数据成员的 id 表达式,除非引用具有前面的初始化并且
    • 它用常量表达式初始化或
    • 它的生命周期开始于评估e

其中任何一个都应该起作用:

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  size_t st = size_of_tuple(tup);
  return st;
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  return size_of_tuple(tup);
}
Run Code Online (Sandbox Code Playgroud)

请注意,Clang 仍会拒绝您的代码,因为您的代码还违反了同一规则。


最终,您的size_of_tupleis_tuple都有缺陷,因为如果它们的参数是引用,则它们不能在常量表达式中使用。如果你想使用这种类似函数的语法,你需要type_c来自 Boost.Hana 的类似语法:

template <typename T>
class type {};
template <typename T>
constexpr type<T> type_c{};

template <typename T>
constexpr bool is_tuple(type<T>) {
    return is_tuple_t<T>::value;
}

template <typename T>
constexpr size_t size_of_tuple(type<T> tup) {
    static_assert(is_tuple(tup), "size_of_tuple argument must be a tuple");
    //...
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  size_t st = size_of_tuple(type_c<std::remove_cvref_t<decltype(tup)>>);
  return st;
}

uint16_t var = 42;
constexpr size_t f2 = foo(std::forward_as_tuple(var));
Run Code Online (Sandbox Code Playgroud)