c++解压缩可变模板参数并调用另一个模板方法



我正在尝试解包可变模板参数,并调用"read<>"在每一个上面。然后收集"read<>"创建一个新的元组对象。

这是我学习"折叠表达"后写的东西。在c++中17:

template<typename ...U>
std::tuple<U...> read_impl(std::tuple<U...>* arg) {
// The argument exists for function overloading. As an alternative of partial specialization.
using T = std::tuple<U...>;
return T(read<U>(), ...);
}

And I got Fatal Error C1001 INTERNAL COMPILER Error .

预期用途:

std::tuple<int, float, double> tup = read_impl(static_cast<std::tuple<int, float, double>*>(0));
// which is equivalent to
std::tuple<int, float, double> tup = std::make_tuple(read<int>(), read<float>(), read<double>());

这就是我目前正在做的事情,如果没有解压魔法,它是丑陋和冗长的。

template<typename U1>
std::tuple<U1> read_impl(std::tuple<U1>* arg) {
using T = std::remove_pointer<decltype(arg)>::type;
return T(read<U1>());
}
template<typename U1, typename U2>
std::tuple<U1, U2> read_impl(std::tuple<U1, U2>* arg) {
using T = std::remove_pointer<decltype(arg)>::type;
return T(read<U1>(), read<U2>());
}
template<typename U1, typename U2, typename U3>
std::tuple<U1, U2, U3> read_impl(std::tuple<U1, U2, U3>* arg) {
using T = std::remove_pointer<decltype(arg)>::type;
return T(read<U1>(), read<U2>(), read<U3>());
}
...

这是一个最小可重复的情况:

#include <tuple>
class Reader {
public:
template<typename T>
T read() {
return read_impl(static_cast<T*>(0));
}
int read_impl(int*) {
return 1;
}
float read_impl(float*) {
return 0.5f;
}
template<typename ...U>
std::tuple<U...> read_impl(std::tuple<U...>* arg) {
using T = std::tuple<U...>;
return T(read<U>(), ...);
}
static void test() {
Reader reader;
auto result = reader.read<std::tuple<int, float>>();
assert(std::get<0>(result) == 1);
assert(std::get<1>(result) == 0.5f);
}
};
int main() {
Reader::test();
return 0;
}

===回复一些评论/回答:===

@kiner_shah为什么一个原始指针指向一个元组?为什么不在read_impl中使用arg呢?

因为如果我想专门化泛型的&;f&;函数的返回值不同,则不会编译。

#include <exception>
#include <assert.h>
class Reader {
public:
template<typename T>
T f() {
throw std::exception();
}
template<>
int f() {
return 1;
}
template<>
float f() {
return 0.5f;
}
static void test_f() {
Reader reader;
auto result = reader.f<int>();
assert(result == 1);
}
};
int main() {
Reader::test_f();
return 0;
}

=======

@Ted Lyngmo你的最小可复制示例不使用任何编译器编译。

这就是我问这个问题的原因。我想让它编译。

下面是一个更完整的Reader示例。它需要能够读取像这样的复杂类型:reader.read<std::unordered_map<int, std::vector<std::string>>>().

#include "pch.h"
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <string>
#include <optional>
class Reader {
public:
template<typename T>
T read() {
return read_impl(static_cast<T*>(0));
}
template<typename T>
T read_impl(T*) {
T::deserialize(*this);
}
uint64_t read_impl(uint64_t*) {
return 1; // this will not be a constant in the real situation.
}
uint32_t read_impl(uint32_t*) {
return 1; // this will not be a constant in the real situation.
}
int read_impl(int*) {
return 1; // this will not be a constant in the real situation.
}
float read_impl(float*) {
return .5f; // this will not be a constant in the real situation.
}
bool read_impl(bool*) {
return true; // this will not be a constant in the real situation.
}
std::string read_impl(std::string*) {
return "readString"; // this will not be a constant in the real situation.
}
template<typename T>
std::vector<T> read_impl(std::vector<T>*) {
size_t size = read<uint64_t>();
std::vector<T> r;
r.reserve(size);
for (size_t i = 0; i < size; i++) {
r.push_back(read<T>());
}
return r;
}
template<typename TKey, typename TValue>
std::unordered_map<TKey, TValue> read_impl(std::unordered_map<TKey, TValue>*) {
size_t size = read<uint64_t>();
std::unordered_map<TKey, TValue> r;
r.reserve(size);
for (size_t i = 0; i < size; i++) {
TKey k = read<TKey>();
TValue v = read<TValue>();
r[k] = v;
}
return r;
}
template<typename T>
std::unordered_set<T> read_impl(std::unordered_set<T>*) {
size_t size = read<uint64_t>();
std::unordered_set<T> r;
r.reserve(size);
for (size_t i = 0; i < size; i++) {
r.insert(read<T>());
}
return r;
}
template<typename ...U>
std::tuple<U...> read_impl(std::tuple<U...>* arg) {
using T = std::tuple<U...>;
return T{ read<U>() ... };
}
template<typename T>
std::optional<T> read_impl(std::optional<T>* t) {
bool hasValue = read<bool>();
std::optional<T> r;
if (hasValue) {
r = read<T>();
}
return r;
}
static void test() {
Reader reader;
auto result = reader.read<std::tuple<int, float>>();
assert(std::get<0>(result) == 1);
assert(std::get<1>(result) == 0.5f);
auto result2 = reader.read<std::unordered_map<int, std::vector<std::string>>>();
assert(result2[1] == std::vector<std::string>{"readString"});
}
};
int main() {
Reader::test();
return 0;
}

=======

@HolyBlackCat和@Jarod42非常感谢你们指出代码中的求值顺序问题。

我将放弃专门化并使用if constexpr。您也不需要指向tuples的指针。

的例子:

class Reader {
public:
template <typename T>
constexpr std::tuple<T> read() {
// return different values depending on type:
if constexpr (std::is_same_v<T, int>) return 1;
if constexpr (std::is_same_v<T, float>) return 0.5f;
return {};
}
// an overload that requires at least 2 template parameters:
template <typename T, typename U, typename... V>
constexpr std::tuple<T, U, V...> read() {
return std::tuple_cat(read<T>(), read<U>(), read<V>()...);
}
static void test() {
Reader reader;
constexpr auto result = reader.read<int, float>();
static_assert(std::is_same_v<decltype(result),
const std::tuple<int, float>>);
static_assert(std::get<0>(result) == 1);
static_assert(std::get<1>(result) == 0.5f);
}
};

演示如果您真的希望保持读取intfloat的实现分开,而不是使用if constexpr,您可以:

template <typename T>
requires std::is_same_v<T, int>
constexpr std::tuple<T> read() {
return 1;
}

template <typename T>
requires std::is_same_v<T, float>
constexpr std::tuple<T> read() {
return 0.5f;
}

如果您需要能够将read不仅转换为tuples,您可以使其更通用,而不需要指向您想要读取的类型的指针。

// type trait to check if a type is a tuple:
template <typename...> struct is_tuple : std::false_type {};
template <typename... T> struct is_tuple<std::tuple<T...>> : std::true_type {};
template<class T>
inline constexpr bool is_tuple_v = is_tuple<T>::value;
class Reader {
public:
template <typename T>
requires std::is_same_v<T, int>
constexpr int read() { return 1; }
template <typename T>
requires std::is_same_v<T, float>
constexpr float read() { return 0.5f; }
// and it supports more complex types, like tuples:
template <typename T>
requires is_tuple_v<T>
constexpr T read() {
return [this]<std::size_t... I>(std::index_sequence<I...>) -> T {
return {this->read<std::tuple_element_t<I, T>>()...};
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
static void test() {/* same as above */ }
};

演示

添加对unordered_maps的支持将遵循与tuples相同的模式:

// type trait:
template <typename...> struct is_unordered_map : std::false_type {};
template<class Key, class T, class Hash, class KeyEqual, class Allocator>
struct is_unordered_map<std::unordered_map<Key,T,Hash,KeyEqual,Allocator>> :
std::true_type {};
template<class T>
inline constexpr bool is_unordered_map_v = is_unordered_map<T>::value;
// ...
template<typename T>
requires is_unordered_map_v<T>
T read() {
using TKey = typename T::key_type;
using TValue = typename T::mapped_type;
std::size_t size = read<std::uint64_t>();
T r;
r.reserve(size);
for (std::size_t i = 0; i < size; i++) {
TKey k = read<TKey>();
TValue v = read<TValue>();
r[k] = v;
//or, if the first Key value read should be kept:
//r.emplace(read<TKey>(), read<TValue>());
}
return r;
}

unordered_map,vectorstring的演示

根据@HolyBlackCat和@Jarod42的评论,T(read<U>(), ...)应该是T{ read<U>() ... }

template<typename ...U>
std::tuple<U...> read_impl(std::tuple<U...>* arg) {
using T = std::tuple<U...>;
return T{ read<U>() ... };
}

我正在阅读折叠表达式功能(https://en.cppreference.com/w/cpp/language/fold),现在我知道了"fold/reduce"与"展开"不一样。

相关内容

  • 没有找到相关文章

最新更新