C++ 使用 std::enable_if 创建最多 10 个参数的 std::tuple 特化
C++ use std::enable_if to create std::tuple specialisations up to 10 arguments
我想创建一个元组,它具有最多 10 个参数的特化,类似于 std::pair 是两个参数的特化。
即 tuple<int,float,bool>
将具有成员 first()
、second()
和 third()
这是我目前的尝试:
#pragma once
#include <tuple>
#include <type_traits>
template<typename... Types>
struct tuple : std::tuple<Types...> {
using std::tuple<Types...>::tuple;
static constexpr size_t size = sizeof...(Types);
template<size_t N>
using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;
template<size_t N>
const elem_n<N>& get() const { return std::get<N>(*this); }
template<size_t N>
elem_n<N>& get() { return std::get<N>(*this); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, const elem_n<0>&> first() const { return get<0>(); }
template<bool F = false> std::enable_if_t< (size >= 2) || F, const elem_n<1>&> second() const { return get<1>(); }
template<bool F = false> std::enable_if_t< (size >= 3) || F, const elem_n<2>&> third() const { return get<2>(); }
template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
template<bool F = false> std::enable_if_t< (size >= 5) || F, const elem_n<4>&> fith() const { return get<4>(); }
template<bool F = false> std::enable_if_t< (size >= 6) || F, const elem_n<5>&> sixth() const { return get<5>(); }
template<bool F = false> std::enable_if_t< (size >= 7) || F, const elem_n<6>&> seventh() const { return get<6>(); }
template<bool F = false> std::enable_if_t< (size >= 8) || F, const elem_n<7>&> eighth() const { return get<7>(); }
template<bool F = false> std::enable_if_t< (size >= 9) || F, const elem_n<8>&> ninth() const { return get<8>(); }
template<bool F = false> std::enable_if_t< (size >= 10) || F, const elem_n<9>&> tenth() const { return get<9>(); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<0>&> first() { return get<0>(); }
template<bool F = false> std::enable_if_t< (size >= 2) || F, elem_n<1>&> second() { return get<1>(); }
template<bool F = false> std::enable_if_t< (size >= 3) || F, elem_n<2>&> third() { return get<2>(); }
template<bool F = false> std::enable_if_t< (size >= 4) || F, elem_n<3>&> fourth() { return get<3>(); }
template<bool F = false> std::enable_if_t< (size >= 5) || F, elem_n<4>&> fith() { return get<4>(); }
template<bool F = false> std::enable_if_t< (size >= 6) || F, elem_n<5>&> sixth() { return get<5>(); }
template<bool F = false> std::enable_if_t< (size >= 7) || F, elem_n<6>&> seventh() { return get<6>(); }
template<bool F = false> std::enable_if_t< (size >= 8) || F, elem_n<7>&> eighth() { return get<7>(); }
template<bool F = false> std::enable_if_t< (size >= 9) || F, elem_n<8>&> ninth() { return get<8>(); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<9>&> tenth() { return get<9>(); }
};
我也尝试过:
template<size_t N>
using elem_n = std::conditional_t<(size >= N), std::tuple_element_t<N, std::tuple<Types...>>, void>;
但是在使用
进行测试时
using my_tripple = tuple<int, std::string, float>;
my_tripple a;
a.first() = 6;
a.second() = "hello";
a.third() = 0.1f;
我得到编译错误:
/usr/include/c++/9/tuple:1303: error: static assertion failed: tuple index is in range
1303 | static_assert(__i < tuple_size<tuple<>>::value,
| ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
从 fourth()
到 tenth()
error: no type named ‘type’ in ‘struct std::tuple_element<3, std::tuple<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, float> >’
26 | template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
| ^~~~~~
这与问题 有关,但该问题的解决方案在这里不起作用。
谢谢
您为 second()
、third()
使用默认模板参数的方法是正确的方向,但您错过的是您需要使 get<>
的模板参数依赖在默认的模板参数上,所以它在模板实例化之前不会被解析,如果它从未被使用,则永远不会被解析。除非它依赖于模板参数,否则它会在声明时得到解析,并且由于给定的原因而失败。
仅使用 second()
的简短示例。 third()
、fourth()
、等。阿尔。将以相同的方式声明,使用 size_t n=2
、size_t n=3
等:
#include <tuple>
template<typename... Types>
struct tuple : std::tuple<Types...> {
using std::tuple<Types...>::tuple;
static constexpr size_t size = sizeof...(Types);
template<size_t N>
using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;
template<size_t N>
const elem_n<N>& get() const { return std::get<N>(*this); }
template<size_t N>
elem_n<N>& get() { return std::get<N>(*this); }
template<size_t n=1> auto second() const { return get<n>(); }
};
tuple<int> foo;
tuple<int, float> bar;
float foobar()
{
return bar.second(); // compiles, foo.first() would be a compilation error
}
在 foo
的情况下,由于 get<1>
从未实际存在,除非 second()
被显式调用,因此没有编译错误。
我想创建一个元组,它具有最多 10 个参数的特化,类似于 std::pair 是两个参数的特化。
即 tuple<int,float,bool>
将具有成员 first()
、second()
和 third()
这是我目前的尝试:
#pragma once
#include <tuple>
#include <type_traits>
template<typename... Types>
struct tuple : std::tuple<Types...> {
using std::tuple<Types...>::tuple;
static constexpr size_t size = sizeof...(Types);
template<size_t N>
using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;
template<size_t N>
const elem_n<N>& get() const { return std::get<N>(*this); }
template<size_t N>
elem_n<N>& get() { return std::get<N>(*this); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, const elem_n<0>&> first() const { return get<0>(); }
template<bool F = false> std::enable_if_t< (size >= 2) || F, const elem_n<1>&> second() const { return get<1>(); }
template<bool F = false> std::enable_if_t< (size >= 3) || F, const elem_n<2>&> third() const { return get<2>(); }
template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
template<bool F = false> std::enable_if_t< (size >= 5) || F, const elem_n<4>&> fith() const { return get<4>(); }
template<bool F = false> std::enable_if_t< (size >= 6) || F, const elem_n<5>&> sixth() const { return get<5>(); }
template<bool F = false> std::enable_if_t< (size >= 7) || F, const elem_n<6>&> seventh() const { return get<6>(); }
template<bool F = false> std::enable_if_t< (size >= 8) || F, const elem_n<7>&> eighth() const { return get<7>(); }
template<bool F = false> std::enable_if_t< (size >= 9) || F, const elem_n<8>&> ninth() const { return get<8>(); }
template<bool F = false> std::enable_if_t< (size >= 10) || F, const elem_n<9>&> tenth() const { return get<9>(); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<0>&> first() { return get<0>(); }
template<bool F = false> std::enable_if_t< (size >= 2) || F, elem_n<1>&> second() { return get<1>(); }
template<bool F = false> std::enable_if_t< (size >= 3) || F, elem_n<2>&> third() { return get<2>(); }
template<bool F = false> std::enable_if_t< (size >= 4) || F, elem_n<3>&> fourth() { return get<3>(); }
template<bool F = false> std::enable_if_t< (size >= 5) || F, elem_n<4>&> fith() { return get<4>(); }
template<bool F = false> std::enable_if_t< (size >= 6) || F, elem_n<5>&> sixth() { return get<5>(); }
template<bool F = false> std::enable_if_t< (size >= 7) || F, elem_n<6>&> seventh() { return get<6>(); }
template<bool F = false> std::enable_if_t< (size >= 8) || F, elem_n<7>&> eighth() { return get<7>(); }
template<bool F = false> std::enable_if_t< (size >= 9) || F, elem_n<8>&> ninth() { return get<8>(); }
template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<9>&> tenth() { return get<9>(); }
};
我也尝试过:
template<size_t N>
using elem_n = std::conditional_t<(size >= N), std::tuple_element_t<N, std::tuple<Types...>>, void>;
但是在使用
进行测试时using my_tripple = tuple<int, std::string, float>;
my_tripple a;
a.first() = 6;
a.second() = "hello";
a.third() = 0.1f;
我得到编译错误:
/usr/include/c++/9/tuple:1303: error: static assertion failed: tuple index is in range
1303 | static_assert(__i < tuple_size<tuple<>>::value,
| ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
从 fourth()
到 tenth()
error: no type named ‘type’ in ‘struct std::tuple_element<3, std::tuple<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, float> >’
26 | template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
| ^~~~~~
这与问题
谢谢
您为 second()
、third()
使用默认模板参数的方法是正确的方向,但您错过的是您需要使 get<>
的模板参数依赖在默认的模板参数上,所以它在模板实例化之前不会被解析,如果它从未被使用,则永远不会被解析。除非它依赖于模板参数,否则它会在声明时得到解析,并且由于给定的原因而失败。
仅使用 second()
的简短示例。 third()
、fourth()
、等。阿尔。将以相同的方式声明,使用 size_t n=2
、size_t n=3
等:
#include <tuple>
template<typename... Types>
struct tuple : std::tuple<Types...> {
using std::tuple<Types...>::tuple;
static constexpr size_t size = sizeof...(Types);
template<size_t N>
using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;
template<size_t N>
const elem_n<N>& get() const { return std::get<N>(*this); }
template<size_t N>
elem_n<N>& get() { return std::get<N>(*this); }
template<size_t n=1> auto second() const { return get<n>(); }
};
tuple<int> foo;
tuple<int, float> bar;
float foobar()
{
return bar.second(); // compiles, foo.first() would be a compilation error
}
在 foo
的情况下,由于 get<1>
从未实际存在,除非 second()
被显式调用,因此没有编译错误。